18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// ChromeOS EC communication protocol helper functions 38c2ecf20Sopenharmony_ci// 48c2ecf20Sopenharmony_ci// Copyright (C) 2015 Google, Inc 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_commands.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_proto.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "cros_ec_trace.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define EC_COMMAND_RETRIES 50 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const int cros_ec_error_map[] = { 198c2ecf20Sopenharmony_ci [EC_RES_INVALID_COMMAND] = -EOPNOTSUPP, 208c2ecf20Sopenharmony_ci [EC_RES_ERROR] = -EIO, 218c2ecf20Sopenharmony_ci [EC_RES_INVALID_PARAM] = -EINVAL, 228c2ecf20Sopenharmony_ci [EC_RES_ACCESS_DENIED] = -EACCES, 238c2ecf20Sopenharmony_ci [EC_RES_INVALID_RESPONSE] = -EPROTO, 248c2ecf20Sopenharmony_ci [EC_RES_INVALID_VERSION] = -ENOPROTOOPT, 258c2ecf20Sopenharmony_ci [EC_RES_INVALID_CHECKSUM] = -EBADMSG, 268c2ecf20Sopenharmony_ci [EC_RES_IN_PROGRESS] = -EINPROGRESS, 278c2ecf20Sopenharmony_ci [EC_RES_UNAVAILABLE] = -ENODATA, 288c2ecf20Sopenharmony_ci [EC_RES_TIMEOUT] = -ETIMEDOUT, 298c2ecf20Sopenharmony_ci [EC_RES_OVERFLOW] = -EOVERFLOW, 308c2ecf20Sopenharmony_ci [EC_RES_INVALID_HEADER] = -EBADR, 318c2ecf20Sopenharmony_ci [EC_RES_REQUEST_TRUNCATED] = -EBADR, 328c2ecf20Sopenharmony_ci [EC_RES_RESPONSE_TOO_BIG] = -EFBIG, 338c2ecf20Sopenharmony_ci [EC_RES_BUS_ERROR] = -EFAULT, 348c2ecf20Sopenharmony_ci [EC_RES_BUSY] = -EBUSY, 358c2ecf20Sopenharmony_ci [EC_RES_INVALID_HEADER_VERSION] = -EBADMSG, 368c2ecf20Sopenharmony_ci [EC_RES_INVALID_HEADER_CRC] = -EBADMSG, 378c2ecf20Sopenharmony_ci [EC_RES_INVALID_DATA_CRC] = -EBADMSG, 388c2ecf20Sopenharmony_ci [EC_RES_DUP_UNAVAILABLE] = -ENODATA, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int cros_ec_map_error(uint32_t result) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci int ret = 0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (result != EC_RES_SUCCESS) { 468c2ecf20Sopenharmony_ci if (result < ARRAY_SIZE(cros_ec_error_map) && cros_ec_error_map[result]) 478c2ecf20Sopenharmony_ci ret = cros_ec_error_map[result]; 488c2ecf20Sopenharmony_ci else 498c2ecf20Sopenharmony_ci ret = -EPROTO; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return ret; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int prepare_packet(struct cros_ec_device *ec_dev, 568c2ecf20Sopenharmony_ci struct cros_ec_command *msg) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct ec_host_request *request; 598c2ecf20Sopenharmony_ci u8 *out; 608c2ecf20Sopenharmony_ci int i; 618c2ecf20Sopenharmony_ci u8 csum = 0; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION); 648c2ecf20Sopenharmony_ci BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci out = ec_dev->dout; 678c2ecf20Sopenharmony_ci request = (struct ec_host_request *)out; 688c2ecf20Sopenharmony_ci request->struct_version = EC_HOST_REQUEST_VERSION; 698c2ecf20Sopenharmony_ci request->checksum = 0; 708c2ecf20Sopenharmony_ci request->command = msg->command; 718c2ecf20Sopenharmony_ci request->command_version = msg->version; 728c2ecf20Sopenharmony_ci request->reserved = 0; 738c2ecf20Sopenharmony_ci request->data_len = msg->outsize; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(*request); i++) 768c2ecf20Sopenharmony_ci csum += out[i]; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* Copy data and update checksum */ 798c2ecf20Sopenharmony_ci memcpy(out + sizeof(*request), msg->data, msg->outsize); 808c2ecf20Sopenharmony_ci for (i = 0; i < msg->outsize; i++) 818c2ecf20Sopenharmony_ci csum += msg->data[i]; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci request->checksum = -csum; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return sizeof(*request) + msg->outsize; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int send_command(struct cros_ec_device *ec_dev, 898c2ecf20Sopenharmony_ci struct cros_ec_command *msg) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int ret; 928c2ecf20Sopenharmony_ci int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (ec_dev->proto_version > 2) 958c2ecf20Sopenharmony_ci xfer_fxn = ec_dev->pkt_xfer; 968c2ecf20Sopenharmony_ci else 978c2ecf20Sopenharmony_ci xfer_fxn = ec_dev->cmd_xfer; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!xfer_fxn) { 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * This error can happen if a communication error happened and 1028c2ecf20Sopenharmony_ci * the EC is trying to use protocol v2, on an underlying 1038c2ecf20Sopenharmony_ci * communication mechanism that does not support v2. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci dev_err_once(ec_dev->dev, 1068c2ecf20Sopenharmony_ci "missing EC transfer API, cannot send command\n"); 1078c2ecf20Sopenharmony_ci return -EIO; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci trace_cros_ec_request_start(msg); 1118c2ecf20Sopenharmony_ci ret = (*xfer_fxn)(ec_dev, msg); 1128c2ecf20Sopenharmony_ci trace_cros_ec_request_done(msg, ret); 1138c2ecf20Sopenharmony_ci if (msg->result == EC_RES_IN_PROGRESS) { 1148c2ecf20Sopenharmony_ci int i; 1158c2ecf20Sopenharmony_ci struct cros_ec_command *status_msg; 1168c2ecf20Sopenharmony_ci struct ec_response_get_comms_status *status; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), 1198c2ecf20Sopenharmony_ci GFP_KERNEL); 1208c2ecf20Sopenharmony_ci if (!status_msg) 1218c2ecf20Sopenharmony_ci return -ENOMEM; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci status_msg->version = 0; 1248c2ecf20Sopenharmony_ci status_msg->command = EC_CMD_GET_COMMS_STATUS; 1258c2ecf20Sopenharmony_ci status_msg->insize = sizeof(*status); 1268c2ecf20Sopenharmony_ci status_msg->outsize = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Query the EC's status until it's no longer busy or 1308c2ecf20Sopenharmony_ci * we encounter an error. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci for (i = 0; i < EC_COMMAND_RETRIES; i++) { 1338c2ecf20Sopenharmony_ci usleep_range(10000, 11000); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci trace_cros_ec_request_start(status_msg); 1368c2ecf20Sopenharmony_ci ret = (*xfer_fxn)(ec_dev, status_msg); 1378c2ecf20Sopenharmony_ci trace_cros_ec_request_done(status_msg, ret); 1388c2ecf20Sopenharmony_ci if (ret == -EAGAIN) 1398c2ecf20Sopenharmony_ci continue; 1408c2ecf20Sopenharmony_ci if (ret < 0) 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci msg->result = status_msg->result; 1448c2ecf20Sopenharmony_ci if (status_msg->result != EC_RES_SUCCESS) 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci status = (struct ec_response_get_comms_status *) 1488c2ecf20Sopenharmony_ci status_msg->data; 1498c2ecf20Sopenharmony_ci if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci kfree(status_msg); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * cros_ec_prepare_tx() - Prepare an outgoing message in the output buffer. 1618c2ecf20Sopenharmony_ci * @ec_dev: Device to register. 1628c2ecf20Sopenharmony_ci * @msg: Message to write. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * This is intended to be used by all ChromeOS EC drivers, but at present 1658c2ecf20Sopenharmony_ci * only SPI uses it. Once LPC uses the same protocol it can start using it. 1668c2ecf20Sopenharmony_ci * I2C could use it now, with a refactor of the existing code. 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Return: 0 on success or negative error code. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ciint cros_ec_prepare_tx(struct cros_ec_device *ec_dev, 1718c2ecf20Sopenharmony_ci struct cros_ec_command *msg) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci u8 *out; 1748c2ecf20Sopenharmony_ci u8 csum; 1758c2ecf20Sopenharmony_ci int i; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (ec_dev->proto_version > 2) 1788c2ecf20Sopenharmony_ci return prepare_packet(ec_dev, msg); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); 1818c2ecf20Sopenharmony_ci out = ec_dev->dout; 1828c2ecf20Sopenharmony_ci out[0] = EC_CMD_VERSION0 + msg->version; 1838c2ecf20Sopenharmony_ci out[1] = msg->command; 1848c2ecf20Sopenharmony_ci out[2] = msg->outsize; 1858c2ecf20Sopenharmony_ci csum = out[0] + out[1] + out[2]; 1868c2ecf20Sopenharmony_ci for (i = 0; i < msg->outsize; i++) 1878c2ecf20Sopenharmony_ci csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; 1888c2ecf20Sopenharmony_ci out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return EC_MSG_TX_PROTO_BYTES + msg->outsize; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cros_ec_prepare_tx); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/** 1958c2ecf20Sopenharmony_ci * cros_ec_check_result() - Check ec_msg->result. 1968c2ecf20Sopenharmony_ci * @ec_dev: EC device. 1978c2ecf20Sopenharmony_ci * @msg: Message to check. 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * This is used by ChromeOS EC drivers to check the ec_msg->result for 2008c2ecf20Sopenharmony_ci * errors and to warn about them. 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Return: 0 on success or negative error code. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ciint cros_ec_check_result(struct cros_ec_device *ec_dev, 2058c2ecf20Sopenharmony_ci struct cros_ec_command *msg) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci switch (msg->result) { 2088c2ecf20Sopenharmony_ci case EC_RES_SUCCESS: 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci case EC_RES_IN_PROGRESS: 2118c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", 2128c2ecf20Sopenharmony_ci msg->command); 2138c2ecf20Sopenharmony_ci return -EAGAIN; 2148c2ecf20Sopenharmony_ci default: 2158c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", 2168c2ecf20Sopenharmony_ci msg->command, msg->result); 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cros_ec_check_result); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* 2238c2ecf20Sopenharmony_ci * cros_ec_get_host_event_wake_mask 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * Get the mask of host events that cause wake from suspend. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * @ec_dev: EC device to call 2288c2ecf20Sopenharmony_ci * @msg: message structure to use 2298c2ecf20Sopenharmony_ci * @mask: result when function returns >=0. 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * LOCKING: 2328c2ecf20Sopenharmony_ci * the caller has ec_dev->lock mutex, or the caller knows there is 2338c2ecf20Sopenharmony_ci * no other command in progress. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistatic int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev, 2368c2ecf20Sopenharmony_ci struct cros_ec_command *msg, 2378c2ecf20Sopenharmony_ci uint32_t *mask) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct ec_response_host_event_mask *r; 2408c2ecf20Sopenharmony_ci int ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci msg->command = EC_CMD_HOST_EVENT_GET_WAKE_MASK; 2438c2ecf20Sopenharmony_ci msg->version = 0; 2448c2ecf20Sopenharmony_ci msg->outsize = 0; 2458c2ecf20Sopenharmony_ci msg->insize = sizeof(*r); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ret = send_command(ec_dev, msg); 2488c2ecf20Sopenharmony_ci if (ret >= 0) { 2498c2ecf20Sopenharmony_ci if (msg->result == EC_RES_INVALID_COMMAND) 2508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2518c2ecf20Sopenharmony_ci if (msg->result != EC_RES_SUCCESS) 2528c2ecf20Sopenharmony_ci return -EPROTO; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci if (ret > 0) { 2558c2ecf20Sopenharmony_ci r = (struct ec_response_host_event_mask *)msg->data; 2568c2ecf20Sopenharmony_ci *mask = r->mask; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev, 2638c2ecf20Sopenharmony_ci int devidx, 2648c2ecf20Sopenharmony_ci struct cros_ec_command *msg) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci /* 2678c2ecf20Sopenharmony_ci * Try using v3+ to query for supported protocols. If this 2688c2ecf20Sopenharmony_ci * command fails, fall back to v2. Returns the highest protocol 2698c2ecf20Sopenharmony_ci * supported by the EC. 2708c2ecf20Sopenharmony_ci * Also sets the max request/response/passthru size. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci int ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (!ec_dev->pkt_xfer) 2758c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci memset(msg, 0, sizeof(*msg)); 2788c2ecf20Sopenharmony_ci msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO; 2798c2ecf20Sopenharmony_ci msg->insize = sizeof(struct ec_response_get_protocol_info); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ret = send_command(ec_dev, msg); 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * Send command once again when timeout occurred. 2848c2ecf20Sopenharmony_ci * Fingerprint MCU (FPMCU) is restarted during system boot which 2858c2ecf20Sopenharmony_ci * introduces small window in which FPMCU won't respond for any 2868c2ecf20Sopenharmony_ci * messages sent by kernel. There is no need to wait before next 2878c2ecf20Sopenharmony_ci * attempt because we waited at least EC_MSG_DEADLINE_MS. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) 2908c2ecf20Sopenharmony_ci ret = send_command(ec_dev, msg); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (ret < 0) { 2938c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, 2948c2ecf20Sopenharmony_ci "failed to check for EC[%d] protocol version: %d\n", 2958c2ecf20Sopenharmony_ci devidx, ret); 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND) 3008c2ecf20Sopenharmony_ci return -ENODEV; 3018c2ecf20Sopenharmony_ci else if (msg->result != EC_RES_SUCCESS) 3028c2ecf20Sopenharmony_ci return msg->result; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct cros_ec_command *msg; 3108c2ecf20Sopenharmony_ci struct ec_params_hello *hello_params; 3118c2ecf20Sopenharmony_ci struct ec_response_hello *hello_response; 3128c2ecf20Sopenharmony_ci int ret; 3138c2ecf20Sopenharmony_ci int len = max(sizeof(*hello_params), sizeof(*hello_response)); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL); 3168c2ecf20Sopenharmony_ci if (!msg) 3178c2ecf20Sopenharmony_ci return -ENOMEM; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci msg->version = 0; 3208c2ecf20Sopenharmony_ci msg->command = EC_CMD_HELLO; 3218c2ecf20Sopenharmony_ci hello_params = (struct ec_params_hello *)msg->data; 3228c2ecf20Sopenharmony_ci msg->outsize = sizeof(*hello_params); 3238c2ecf20Sopenharmony_ci hello_response = (struct ec_response_hello *)msg->data; 3248c2ecf20Sopenharmony_ci msg->insize = sizeof(*hello_response); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci hello_params->in_data = 0xa0b0c0d0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ret = send_command(ec_dev, msg); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (ret < 0) { 3318c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, 3328c2ecf20Sopenharmony_ci "EC failed to respond to v2 hello: %d\n", 3338c2ecf20Sopenharmony_ci ret); 3348c2ecf20Sopenharmony_ci goto exit; 3358c2ecf20Sopenharmony_ci } else if (msg->result != EC_RES_SUCCESS) { 3368c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, 3378c2ecf20Sopenharmony_ci "EC responded to v2 hello with error: %d\n", 3388c2ecf20Sopenharmony_ci msg->result); 3398c2ecf20Sopenharmony_ci ret = msg->result; 3408c2ecf20Sopenharmony_ci goto exit; 3418c2ecf20Sopenharmony_ci } else if (hello_response->out_data != 0xa1b2c3d4) { 3428c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, 3438c2ecf20Sopenharmony_ci "EC responded to v2 hello with bad result: %u\n", 3448c2ecf20Sopenharmony_ci hello_response->out_data); 3458c2ecf20Sopenharmony_ci ret = -EBADMSG; 3468c2ecf20Sopenharmony_ci goto exit; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci exit: 3528c2ecf20Sopenharmony_ci kfree(msg); 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/* 3578c2ecf20Sopenharmony_ci * cros_ec_get_host_command_version_mask 3588c2ecf20Sopenharmony_ci * 3598c2ecf20Sopenharmony_ci * Get the version mask of a given command. 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * @ec_dev: EC device to call 3628c2ecf20Sopenharmony_ci * @msg: message structure to use 3638c2ecf20Sopenharmony_ci * @cmd: command to get the version of. 3648c2ecf20Sopenharmony_ci * @mask: result when function returns 0. 3658c2ecf20Sopenharmony_ci * 3668c2ecf20Sopenharmony_ci * @return 0 on success, error code otherwise 3678c2ecf20Sopenharmony_ci * 3688c2ecf20Sopenharmony_ci * LOCKING: 3698c2ecf20Sopenharmony_ci * the caller has ec_dev->lock mutex or the caller knows there is 3708c2ecf20Sopenharmony_ci * no other command in progress. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_cistatic int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, 3738c2ecf20Sopenharmony_ci u16 cmd, u32 *mask) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct ec_params_get_cmd_versions *pver; 3768c2ecf20Sopenharmony_ci struct ec_response_get_cmd_versions *rver; 3778c2ecf20Sopenharmony_ci struct cros_ec_command *msg; 3788c2ecf20Sopenharmony_ci int ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci msg = kmalloc(sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)), 3818c2ecf20Sopenharmony_ci GFP_KERNEL); 3828c2ecf20Sopenharmony_ci if (!msg) 3838c2ecf20Sopenharmony_ci return -ENOMEM; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci msg->version = 0; 3868c2ecf20Sopenharmony_ci msg->command = EC_CMD_GET_CMD_VERSIONS; 3878c2ecf20Sopenharmony_ci msg->insize = sizeof(*rver); 3888c2ecf20Sopenharmony_ci msg->outsize = sizeof(*pver); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci pver = (struct ec_params_get_cmd_versions *)msg->data; 3918c2ecf20Sopenharmony_ci pver->cmd = cmd; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ret = send_command(ec_dev, msg); 3948c2ecf20Sopenharmony_ci if (ret > 0) { 3958c2ecf20Sopenharmony_ci rver = (struct ec_response_get_cmd_versions *)msg->data; 3968c2ecf20Sopenharmony_ci *mask = rver->version_mask; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci kfree(msg); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/** 4058c2ecf20Sopenharmony_ci * cros_ec_query_all() - Query the protocol version supported by the 4068c2ecf20Sopenharmony_ci * ChromeOS EC. 4078c2ecf20Sopenharmony_ci * @ec_dev: Device to register. 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Return: 0 on success or negative error code. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ciint cros_ec_query_all(struct cros_ec_device *ec_dev) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct device *dev = ec_dev->dev; 4148c2ecf20Sopenharmony_ci struct cros_ec_command *proto_msg; 4158c2ecf20Sopenharmony_ci struct ec_response_get_protocol_info *proto_info; 4168c2ecf20Sopenharmony_ci u32 ver_mask = 0; 4178c2ecf20Sopenharmony_ci int ret; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info), 4208c2ecf20Sopenharmony_ci GFP_KERNEL); 4218c2ecf20Sopenharmony_ci if (!proto_msg) 4228c2ecf20Sopenharmony_ci return -ENOMEM; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* First try sending with proto v3. */ 4258c2ecf20Sopenharmony_ci ec_dev->proto_version = 3; 4268c2ecf20Sopenharmony_ci ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (ret == 0) { 4298c2ecf20Sopenharmony_ci proto_info = (struct ec_response_get_protocol_info *) 4308c2ecf20Sopenharmony_ci proto_msg->data; 4318c2ecf20Sopenharmony_ci ec_dev->max_request = proto_info->max_request_packet_size - 4328c2ecf20Sopenharmony_ci sizeof(struct ec_host_request); 4338c2ecf20Sopenharmony_ci ec_dev->max_response = proto_info->max_response_packet_size - 4348c2ecf20Sopenharmony_ci sizeof(struct ec_host_response); 4358c2ecf20Sopenharmony_ci ec_dev->proto_version = 4368c2ecf20Sopenharmony_ci min(EC_HOST_REQUEST_VERSION, 4378c2ecf20Sopenharmony_ci fls(proto_info->protocol_versions) - 1); 4388c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, 4398c2ecf20Sopenharmony_ci "using proto v%u\n", 4408c2ecf20Sopenharmony_ci ec_dev->proto_version); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ec_dev->din_size = ec_dev->max_response + 4438c2ecf20Sopenharmony_ci sizeof(struct ec_host_response) + 4448c2ecf20Sopenharmony_ci EC_MAX_RESPONSE_OVERHEAD; 4458c2ecf20Sopenharmony_ci ec_dev->dout_size = ec_dev->max_request + 4468c2ecf20Sopenharmony_ci sizeof(struct ec_host_request) + 4478c2ecf20Sopenharmony_ci EC_MAX_REQUEST_OVERHEAD; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * Check for PD 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (ret) { 4558c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret); 4568c2ecf20Sopenharmony_ci ec_dev->max_passthru = 0; 4578c2ecf20Sopenharmony_ci } else { 4588c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "found PD chip\n"); 4598c2ecf20Sopenharmony_ci ec_dev->max_passthru = 4608c2ecf20Sopenharmony_ci proto_info->max_request_packet_size - 4618c2ecf20Sopenharmony_ci sizeof(struct ec_host_request); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci } else { 4648c2ecf20Sopenharmony_ci /* Try querying with a v2 hello message. */ 4658c2ecf20Sopenharmony_ci ec_dev->proto_version = 2; 4668c2ecf20Sopenharmony_ci ret = cros_ec_host_command_proto_query_v2(ec_dev); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (ret == 0) { 4698c2ecf20Sopenharmony_ci /* V2 hello succeeded. */ 4708c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "falling back to proto v2\n"); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE; 4738c2ecf20Sopenharmony_ci ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE; 4748c2ecf20Sopenharmony_ci ec_dev->max_passthru = 0; 4758c2ecf20Sopenharmony_ci ec_dev->pkt_xfer = NULL; 4768c2ecf20Sopenharmony_ci ec_dev->din_size = EC_PROTO2_MSG_BYTES; 4778c2ecf20Sopenharmony_ci ec_dev->dout_size = EC_PROTO2_MSG_BYTES; 4788c2ecf20Sopenharmony_ci } else { 4798c2ecf20Sopenharmony_ci /* 4808c2ecf20Sopenharmony_ci * It's possible for a test to occur too early when 4818c2ecf20Sopenharmony_ci * the EC isn't listening. If this happens, we'll 4828c2ecf20Sopenharmony_ci * test later when the first command is run. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN; 4858c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret); 4868c2ecf20Sopenharmony_ci goto exit; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci devm_kfree(dev, ec_dev->din); 4918c2ecf20Sopenharmony_ci devm_kfree(dev, ec_dev->dout); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 4948c2ecf20Sopenharmony_ci if (!ec_dev->din) { 4958c2ecf20Sopenharmony_ci ret = -ENOMEM; 4968c2ecf20Sopenharmony_ci goto exit; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 5008c2ecf20Sopenharmony_ci if (!ec_dev->dout) { 5018c2ecf20Sopenharmony_ci devm_kfree(dev, ec_dev->din); 5028c2ecf20Sopenharmony_ci ret = -ENOMEM; 5038c2ecf20Sopenharmony_ci goto exit; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* Probe if MKBP event is supported */ 5078c2ecf20Sopenharmony_ci ret = cros_ec_get_host_command_version_mask(ec_dev, 5088c2ecf20Sopenharmony_ci EC_CMD_GET_NEXT_EVENT, 5098c2ecf20Sopenharmony_ci &ver_mask); 5108c2ecf20Sopenharmony_ci if (ret < 0 || ver_mask == 0) { 5118c2ecf20Sopenharmony_ci ec_dev->mkbp_event_supported = 0; 5128c2ecf20Sopenharmony_ci } else { 5138c2ecf20Sopenharmony_ci ec_dev->mkbp_event_supported = fls(ver_mask); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "MKBP support version %u\n", ec_dev->mkbp_event_supported - 1); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Probe if host sleep v1 is supported for S0ix failure detection. */ 5198c2ecf20Sopenharmony_ci ret = cros_ec_get_host_command_version_mask(ec_dev, 5208c2ecf20Sopenharmony_ci EC_CMD_HOST_SLEEP_EVENT, 5218c2ecf20Sopenharmony_ci &ver_mask); 5228c2ecf20Sopenharmony_ci ec_dev->host_sleep_v1 = (ret >= 0 && (ver_mask & EC_VER_MASK(1))); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Get host event wake mask. */ 5258c2ecf20Sopenharmony_ci ret = cros_ec_get_host_event_wake_mask(ec_dev, proto_msg, 5268c2ecf20Sopenharmony_ci &ec_dev->host_event_wake_mask); 5278c2ecf20Sopenharmony_ci if (ret < 0) { 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * If the EC doesn't support EC_CMD_HOST_EVENT_GET_WAKE_MASK, 5308c2ecf20Sopenharmony_ci * use a reasonable default. Note that we ignore various 5318c2ecf20Sopenharmony_ci * battery, AC status, and power-state events, because (a) 5328c2ecf20Sopenharmony_ci * those can be quite common (e.g., when sitting at full 5338c2ecf20Sopenharmony_ci * charge, on AC) and (b) these are not actionable wake events; 5348c2ecf20Sopenharmony_ci * if anything, we'd like to continue suspending (to save 5358c2ecf20Sopenharmony_ci * power), not wake up. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci ec_dev->host_event_wake_mask = U32_MAX & 5388c2ecf20Sopenharmony_ci ~(EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED) | 5398c2ecf20Sopenharmony_ci EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED) | 5408c2ecf20Sopenharmony_ci EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_LOW) | 5418c2ecf20Sopenharmony_ci EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_CRITICAL) | 5428c2ecf20Sopenharmony_ci EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY) | 5438c2ecf20Sopenharmony_ci EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) | 5448c2ecf20Sopenharmony_ci EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_STATUS)); 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * Old ECs may not support this command. Complain about all 5478c2ecf20Sopenharmony_ci * other errors. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci if (ret != -EOPNOTSUPP) 5508c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, 5518c2ecf20Sopenharmony_ci "failed to retrieve wake mask: %d\n", ret); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ret = 0; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ciexit: 5578c2ecf20Sopenharmony_ci kfree(proto_msg); 5588c2ecf20Sopenharmony_ci return ret; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cros_ec_query_all); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/** 5638c2ecf20Sopenharmony_ci * cros_ec_cmd_xfer() - Send a command to the ChromeOS EC. 5648c2ecf20Sopenharmony_ci * @ec_dev: EC device. 5658c2ecf20Sopenharmony_ci * @msg: Message to write. 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * Call this to send a command to the ChromeOS EC. This should be used instead 5688c2ecf20Sopenharmony_ci * of calling the EC's cmd_xfer() callback directly. This function does not 5698c2ecf20Sopenharmony_ci * convert EC command execution error codes to Linux error codes. Most 5708c2ecf20Sopenharmony_ci * in-kernel users will want to use cros_ec_cmd_xfer_status() instead since 5718c2ecf20Sopenharmony_ci * that function implements the conversion. 5728c2ecf20Sopenharmony_ci * 5738c2ecf20Sopenharmony_ci * Return: 5748c2ecf20Sopenharmony_ci * >0 - EC command was executed successfully. The return value is the number 5758c2ecf20Sopenharmony_ci * of bytes returned by the EC (excluding the header). 5768c2ecf20Sopenharmony_ci * =0 - EC communication was successful. EC command execution results are 5778c2ecf20Sopenharmony_ci * reported in msg->result. The result will be EC_RES_SUCCESS if the 5788c2ecf20Sopenharmony_ci * command was executed successfully or report an EC command execution 5798c2ecf20Sopenharmony_ci * error. 5808c2ecf20Sopenharmony_ci * <0 - EC communication error. Return value is the Linux error code. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ciint cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci int ret; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci mutex_lock(&ec_dev->lock); 5878c2ecf20Sopenharmony_ci if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) { 5888c2ecf20Sopenharmony_ci ret = cros_ec_query_all(ec_dev); 5898c2ecf20Sopenharmony_ci if (ret) { 5908c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, 5918c2ecf20Sopenharmony_ci "EC version unknown and query failed; aborting command\n"); 5928c2ecf20Sopenharmony_ci mutex_unlock(&ec_dev->lock); 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (msg->insize > ec_dev->max_response) { 5988c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "clamping message receive buffer\n"); 5998c2ecf20Sopenharmony_ci msg->insize = ec_dev->max_response; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) { 6038c2ecf20Sopenharmony_ci if (msg->outsize > ec_dev->max_request) { 6048c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, 6058c2ecf20Sopenharmony_ci "request of size %u is too big (max: %u)\n", 6068c2ecf20Sopenharmony_ci msg->outsize, 6078c2ecf20Sopenharmony_ci ec_dev->max_request); 6088c2ecf20Sopenharmony_ci mutex_unlock(&ec_dev->lock); 6098c2ecf20Sopenharmony_ci return -EMSGSIZE; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci if (msg->outsize > ec_dev->max_passthru) { 6138c2ecf20Sopenharmony_ci dev_err(ec_dev->dev, 6148c2ecf20Sopenharmony_ci "passthru rq of size %u is too big (max: %u)\n", 6158c2ecf20Sopenharmony_ci msg->outsize, 6168c2ecf20Sopenharmony_ci ec_dev->max_passthru); 6178c2ecf20Sopenharmony_ci mutex_unlock(&ec_dev->lock); 6188c2ecf20Sopenharmony_ci return -EMSGSIZE; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ret = send_command(ec_dev, msg); 6238c2ecf20Sopenharmony_ci mutex_unlock(&ec_dev->lock); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return ret; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cros_ec_cmd_xfer); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/** 6308c2ecf20Sopenharmony_ci * cros_ec_cmd_xfer_status() - Send a command to the ChromeOS EC. 6318c2ecf20Sopenharmony_ci * @ec_dev: EC device. 6328c2ecf20Sopenharmony_ci * @msg: Message to write. 6338c2ecf20Sopenharmony_ci * 6348c2ecf20Sopenharmony_ci * Call this to send a command to the ChromeOS EC. This should be used instead of calling the EC's 6358c2ecf20Sopenharmony_ci * cmd_xfer() callback directly. It returns success status only if both the command was transmitted 6368c2ecf20Sopenharmony_ci * successfully and the EC replied with success status. 6378c2ecf20Sopenharmony_ci * 6388c2ecf20Sopenharmony_ci * Return: 6398c2ecf20Sopenharmony_ci * >=0 - The number of bytes transferred. 6408c2ecf20Sopenharmony_ci * <0 - Linux error code 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_ciint cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, 6438c2ecf20Sopenharmony_ci struct cros_ec_command *msg) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci int ret, mapped; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci ret = cros_ec_cmd_xfer(ec_dev, msg); 6488c2ecf20Sopenharmony_ci if (ret < 0) 6498c2ecf20Sopenharmony_ci return ret; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci mapped = cros_ec_map_error(msg->result); 6528c2ecf20Sopenharmony_ci if (mapped) { 6538c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "Command result (err: %d [%d])\n", 6548c2ecf20Sopenharmony_ci msg->result, mapped); 6558c2ecf20Sopenharmony_ci ret = mapped; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return ret; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cros_ec_cmd_xfer_status); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic int get_next_event_xfer(struct cros_ec_device *ec_dev, 6638c2ecf20Sopenharmony_ci struct cros_ec_command *msg, 6648c2ecf20Sopenharmony_ci struct ec_response_get_next_event_v1 *event, 6658c2ecf20Sopenharmony_ci int version, uint32_t size) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci int ret; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci msg->version = version; 6708c2ecf20Sopenharmony_ci msg->command = EC_CMD_GET_NEXT_EVENT; 6718c2ecf20Sopenharmony_ci msg->insize = size; 6728c2ecf20Sopenharmony_ci msg->outsize = 0; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci ret = cros_ec_cmd_xfer_status(ec_dev, msg); 6758c2ecf20Sopenharmony_ci if (ret > 0) { 6768c2ecf20Sopenharmony_ci ec_dev->event_size = ret - 1; 6778c2ecf20Sopenharmony_ci ec_dev->event_data = *event; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci return ret; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic int get_next_event(struct cros_ec_device *ec_dev) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct { 6868c2ecf20Sopenharmony_ci struct cros_ec_command msg; 6878c2ecf20Sopenharmony_ci struct ec_response_get_next_event_v1 event; 6888c2ecf20Sopenharmony_ci } __packed buf; 6898c2ecf20Sopenharmony_ci struct cros_ec_command *msg = &buf.msg; 6908c2ecf20Sopenharmony_ci struct ec_response_get_next_event_v1 *event = &buf.event; 6918c2ecf20Sopenharmony_ci const int cmd_version = ec_dev->mkbp_event_supported - 1; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci memset(msg, 0, sizeof(*msg)); 6948c2ecf20Sopenharmony_ci if (ec_dev->suspended) { 6958c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "Device suspended.\n"); 6968c2ecf20Sopenharmony_ci return -EHOSTDOWN; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (cmd_version == 0) 7008c2ecf20Sopenharmony_ci return get_next_event_xfer(ec_dev, msg, event, 0, 7018c2ecf20Sopenharmony_ci sizeof(struct ec_response_get_next_event)); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return get_next_event_xfer(ec_dev, msg, event, cmd_version, 7048c2ecf20Sopenharmony_ci sizeof(struct ec_response_get_next_event_v1)); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int get_keyboard_state_event(struct cros_ec_device *ec_dev) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci u8 buffer[sizeof(struct cros_ec_command) + 7108c2ecf20Sopenharmony_ci sizeof(ec_dev->event_data.data)]; 7118c2ecf20Sopenharmony_ci struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci msg->version = 0; 7148c2ecf20Sopenharmony_ci msg->command = EC_CMD_MKBP_STATE; 7158c2ecf20Sopenharmony_ci msg->insize = sizeof(ec_dev->event_data.data); 7168c2ecf20Sopenharmony_ci msg->outsize = 0; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci ec_dev->event_size = cros_ec_cmd_xfer_status(ec_dev, msg); 7198c2ecf20Sopenharmony_ci ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX; 7208c2ecf20Sopenharmony_ci memcpy(&ec_dev->event_data.data, msg->data, 7218c2ecf20Sopenharmony_ci sizeof(ec_dev->event_data.data)); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return ec_dev->event_size; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/** 7278c2ecf20Sopenharmony_ci * cros_ec_get_next_event() - Fetch next event from the ChromeOS EC. 7288c2ecf20Sopenharmony_ci * @ec_dev: Device to fetch event from. 7298c2ecf20Sopenharmony_ci * @wake_event: Pointer to a bool set to true upon return if the event might be 7308c2ecf20Sopenharmony_ci * treated as a wake event. Ignored if null. 7318c2ecf20Sopenharmony_ci * @has_more_events: Pointer to bool set to true if more than one event is 7328c2ecf20Sopenharmony_ci * pending. 7338c2ecf20Sopenharmony_ci * Some EC will set this flag to indicate cros_ec_get_next_event() 7348c2ecf20Sopenharmony_ci * can be called multiple times in a row. 7358c2ecf20Sopenharmony_ci * It is an optimization to prevent issuing a EC command for 7368c2ecf20Sopenharmony_ci * nothing or wait for another interrupt from the EC to process 7378c2ecf20Sopenharmony_ci * the next message. 7388c2ecf20Sopenharmony_ci * Ignored if null. 7398c2ecf20Sopenharmony_ci * 7408c2ecf20Sopenharmony_ci * Return: negative error code on errors; 0 for no data; or else number of 7418c2ecf20Sopenharmony_ci * bytes received (i.e., an event was retrieved successfully). Event types are 7428c2ecf20Sopenharmony_ci * written out to @ec_dev->event_data.event_type on success. 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ciint cros_ec_get_next_event(struct cros_ec_device *ec_dev, 7458c2ecf20Sopenharmony_ci bool *wake_event, 7468c2ecf20Sopenharmony_ci bool *has_more_events) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci u8 event_type; 7498c2ecf20Sopenharmony_ci u32 host_event; 7508c2ecf20Sopenharmony_ci int ret; 7518c2ecf20Sopenharmony_ci u32 ver_mask; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* 7548c2ecf20Sopenharmony_ci * Default value for wake_event. 7558c2ecf20Sopenharmony_ci * Wake up on keyboard event, wake up for spurious interrupt or link 7568c2ecf20Sopenharmony_ci * error to the EC. 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_ci if (wake_event) 7598c2ecf20Sopenharmony_ci *wake_event = true; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* 7628c2ecf20Sopenharmony_ci * Default value for has_more_events. 7638c2ecf20Sopenharmony_ci * EC will raise another interrupt if AP does not process all events 7648c2ecf20Sopenharmony_ci * anyway. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ci if (has_more_events) 7678c2ecf20Sopenharmony_ci *has_more_events = false; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (!ec_dev->mkbp_event_supported) 7708c2ecf20Sopenharmony_ci return get_keyboard_state_event(ec_dev); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci ret = get_next_event(ec_dev); 7738c2ecf20Sopenharmony_ci /* 7748c2ecf20Sopenharmony_ci * -ENOPROTOOPT is returned when EC returns EC_RES_INVALID_VERSION. 7758c2ecf20Sopenharmony_ci * This can occur when EC based device (e.g. Fingerprint MCU) jumps to 7768c2ecf20Sopenharmony_ci * the RO image which doesn't support newer version of the command. In 7778c2ecf20Sopenharmony_ci * this case we will attempt to update maximum supported version of the 7788c2ecf20Sopenharmony_ci * EC_CMD_GET_NEXT_EVENT. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_ci if (ret == -ENOPROTOOPT) { 7818c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, 7828c2ecf20Sopenharmony_ci "GET_NEXT_EVENT returned invalid version error.\n"); 7838c2ecf20Sopenharmony_ci ret = cros_ec_get_host_command_version_mask(ec_dev, 7848c2ecf20Sopenharmony_ci EC_CMD_GET_NEXT_EVENT, 7858c2ecf20Sopenharmony_ci &ver_mask); 7868c2ecf20Sopenharmony_ci if (ret < 0 || ver_mask == 0) 7878c2ecf20Sopenharmony_ci /* 7888c2ecf20Sopenharmony_ci * Do not change the MKBP supported version if we can't 7898c2ecf20Sopenharmony_ci * obtain supported version correctly. Please note that 7908c2ecf20Sopenharmony_ci * calling EC_CMD_GET_NEXT_EVENT returned 7918c2ecf20Sopenharmony_ci * EC_RES_INVALID_VERSION which means that the command 7928c2ecf20Sopenharmony_ci * is present. 7938c2ecf20Sopenharmony_ci */ 7948c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ec_dev->mkbp_event_supported = fls(ver_mask); 7978c2ecf20Sopenharmony_ci dev_dbg(ec_dev->dev, "MKBP support version changed to %u\n", 7988c2ecf20Sopenharmony_ci ec_dev->mkbp_event_supported - 1); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* Try to get next event with new MKBP support version set. */ 8018c2ecf20Sopenharmony_ci ret = get_next_event(ec_dev); 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (ret <= 0) 8058c2ecf20Sopenharmony_ci return ret; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (has_more_events) 8088c2ecf20Sopenharmony_ci *has_more_events = ec_dev->event_data.event_type & 8098c2ecf20Sopenharmony_ci EC_MKBP_HAS_MORE_EVENTS; 8108c2ecf20Sopenharmony_ci ec_dev->event_data.event_type &= EC_MKBP_EVENT_TYPE_MASK; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (wake_event) { 8138c2ecf20Sopenharmony_ci event_type = ec_dev->event_data.event_type; 8148c2ecf20Sopenharmony_ci host_event = cros_ec_get_host_event(ec_dev); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* 8178c2ecf20Sopenharmony_ci * Sensor events need to be parsed by the sensor sub-device. 8188c2ecf20Sopenharmony_ci * Defer them, and don't report the wakeup here. 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_ci if (event_type == EC_MKBP_EVENT_SENSOR_FIFO) 8218c2ecf20Sopenharmony_ci *wake_event = false; 8228c2ecf20Sopenharmony_ci /* Masked host-events should not count as wake events. */ 8238c2ecf20Sopenharmony_ci else if (host_event && 8248c2ecf20Sopenharmony_ci !(host_event & ec_dev->host_event_wake_mask)) 8258c2ecf20Sopenharmony_ci *wake_event = false; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cros_ec_get_next_event); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci/** 8338c2ecf20Sopenharmony_ci * cros_ec_get_host_event() - Return a mask of event set by the ChromeOS EC. 8348c2ecf20Sopenharmony_ci * @ec_dev: Device to fetch event from. 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * When MKBP is supported, when the EC raises an interrupt, we collect the 8378c2ecf20Sopenharmony_ci * events raised and call the functions in the ec notifier. This function 8388c2ecf20Sopenharmony_ci * is a helper to know which events are raised. 8398c2ecf20Sopenharmony_ci * 8408c2ecf20Sopenharmony_ci * Return: 0 on error or non-zero bitmask of one or more EC_HOST_EVENT_*. 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_ciu32 cros_ec_get_host_event(struct cros_ec_device *ec_dev) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci u32 host_event; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci BUG_ON(!ec_dev->mkbp_event_supported); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT) 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (ec_dev->event_size != sizeof(host_event)) { 8528c2ecf20Sopenharmony_ci dev_warn(ec_dev->dev, "Invalid host event size\n"); 8538c2ecf20Sopenharmony_ci return 0; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci host_event = get_unaligned_le32(&ec_dev->event_data.data.host_event); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci return host_event; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cros_ec_get_host_event); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci/** 8638c2ecf20Sopenharmony_ci * cros_ec_check_features() - Test for the presence of EC features 8648c2ecf20Sopenharmony_ci * 8658c2ecf20Sopenharmony_ci * @ec: EC device, does not have to be connected directly to the AP, 8668c2ecf20Sopenharmony_ci * can be daisy chained through another device. 8678c2ecf20Sopenharmony_ci * @feature: One of ec_feature_code bit. 8688c2ecf20Sopenharmony_ci * 8698c2ecf20Sopenharmony_ci * Call this function to test whether the ChromeOS EC supports a feature. 8708c2ecf20Sopenharmony_ci * 8718c2ecf20Sopenharmony_ci * Return: 1 if supported, 0 if not 8728c2ecf20Sopenharmony_ci */ 8738c2ecf20Sopenharmony_ciint cros_ec_check_features(struct cros_ec_dev *ec, int feature) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci struct cros_ec_command *msg; 8768c2ecf20Sopenharmony_ci int ret; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (ec->features[0] == -1U && ec->features[1] == -1U) { 8798c2ecf20Sopenharmony_ci /* features bitmap not read yet */ 8808c2ecf20Sopenharmony_ci msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); 8818c2ecf20Sopenharmony_ci if (!msg) 8828c2ecf20Sopenharmony_ci return -ENOMEM; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; 8858c2ecf20Sopenharmony_ci msg->insize = sizeof(ec->features); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 8888c2ecf20Sopenharmony_ci if (ret < 0) { 8898c2ecf20Sopenharmony_ci dev_warn(ec->dev, "cannot get EC features: %d/%d\n", 8908c2ecf20Sopenharmony_ci ret, msg->result); 8918c2ecf20Sopenharmony_ci memset(ec->features, 0, sizeof(ec->features)); 8928c2ecf20Sopenharmony_ci } else { 8938c2ecf20Sopenharmony_ci memcpy(ec->features, msg->data, sizeof(ec->features)); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "EC features %08x %08x\n", 8978c2ecf20Sopenharmony_ci ec->features[0], ec->features[1]); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci kfree(msg); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cros_ec_check_features); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/** 9078c2ecf20Sopenharmony_ci * cros_ec_get_sensor_count() - Return the number of MEMS sensors supported. 9088c2ecf20Sopenharmony_ci * 9098c2ecf20Sopenharmony_ci * @ec: EC device, does not have to be connected directly to the AP, 9108c2ecf20Sopenharmony_ci * can be daisy chained through another device. 9118c2ecf20Sopenharmony_ci * Return: < 0 in case of error. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_ciint cros_ec_get_sensor_count(struct cros_ec_dev *ec) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci /* 9168c2ecf20Sopenharmony_ci * Issue a command to get the number of sensor reported. 9178c2ecf20Sopenharmony_ci * If not supported, check for legacy mode. 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_ci int ret, sensor_count; 9208c2ecf20Sopenharmony_ci struct ec_params_motion_sense *params; 9218c2ecf20Sopenharmony_ci struct ec_response_motion_sense *resp; 9228c2ecf20Sopenharmony_ci struct cros_ec_command *msg; 9238c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = ec->ec_dev; 9248c2ecf20Sopenharmony_ci u8 status; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*resp)), 9278c2ecf20Sopenharmony_ci GFP_KERNEL); 9288c2ecf20Sopenharmony_ci if (!msg) 9298c2ecf20Sopenharmony_ci return -ENOMEM; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci msg->version = 1; 9328c2ecf20Sopenharmony_ci msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 9338c2ecf20Sopenharmony_ci msg->outsize = sizeof(*params); 9348c2ecf20Sopenharmony_ci msg->insize = sizeof(*resp); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci params = (struct ec_params_motion_sense *)msg->data; 9378c2ecf20Sopenharmony_ci params->cmd = MOTIONSENSE_CMD_DUMP; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 9408c2ecf20Sopenharmony_ci if (ret < 0) { 9418c2ecf20Sopenharmony_ci sensor_count = ret; 9428c2ecf20Sopenharmony_ci } else { 9438c2ecf20Sopenharmony_ci resp = (struct ec_response_motion_sense *)msg->data; 9448c2ecf20Sopenharmony_ci sensor_count = resp->dump.sensor_count; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci kfree(msg); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* 9498c2ecf20Sopenharmony_ci * Check legacy mode: Let's find out if sensors are accessible 9508c2ecf20Sopenharmony_ci * via LPC interface. 9518c2ecf20Sopenharmony_ci */ 9528c2ecf20Sopenharmony_ci if (sensor_count < 0 && ec->cmd_offset == 0 && ec_dev->cmd_readmem) { 9538c2ecf20Sopenharmony_ci ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, 9548c2ecf20Sopenharmony_ci 1, &status); 9558c2ecf20Sopenharmony_ci if (ret >= 0 && 9568c2ecf20Sopenharmony_ci (status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) { 9578c2ecf20Sopenharmony_ci /* 9588c2ecf20Sopenharmony_ci * We have 2 sensors, one in the lid, one in the base. 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci sensor_count = 2; 9618c2ecf20Sopenharmony_ci } else { 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * EC uses LPC interface and no sensors are presented. 9648c2ecf20Sopenharmony_ci */ 9658c2ecf20Sopenharmony_ci sensor_count = 0; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci return sensor_count; 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cros_ec_get_sensor_count); 971