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