18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Mailbox interface for Wilco Embedded Controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2018 Google LLC 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The Wilco EC is similar to a typical ChromeOS embedded controller. 88c2ecf20Sopenharmony_ci * It uses the same MEC based low-level communication and a similar 98c2ecf20Sopenharmony_ci * protocol, but with some important differences. The EC firmware does 108c2ecf20Sopenharmony_ci * not support the same mailbox commands so it is not registered as a 118c2ecf20Sopenharmony_ci * cros_ec device type. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Most messages follow a standard format, but there are some exceptions 148c2ecf20Sopenharmony_ci * and an interface is provided to do direct/raw transactions that do not 158c2ecf20Sopenharmony_ci * make assumptions about byte placement. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/device.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_data/wilco-ec.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "../cros_ec_lpc_mec.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Version of mailbox interface */ 278c2ecf20Sopenharmony_ci#define EC_MAILBOX_VERSION 0 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Command to start mailbox transaction */ 308c2ecf20Sopenharmony_ci#define EC_MAILBOX_START_COMMAND 0xda 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Version of EC protocol */ 338c2ecf20Sopenharmony_ci#define EC_MAILBOX_PROTO_VERSION 3 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Number of header bytes to be counted as data bytes */ 368c2ecf20Sopenharmony_ci#define EC_MAILBOX_DATA_EXTRA 2 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Maximum timeout */ 398c2ecf20Sopenharmony_ci#define EC_MAILBOX_TIMEOUT HZ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* EC response flags */ 428c2ecf20Sopenharmony_ci#define EC_CMDR_DATA BIT(0) /* Data ready for host to read */ 438c2ecf20Sopenharmony_ci#define EC_CMDR_PENDING BIT(1) /* Write pending to EC */ 448c2ecf20Sopenharmony_ci#define EC_CMDR_BUSY BIT(2) /* EC is busy processing a command */ 458c2ecf20Sopenharmony_ci#define EC_CMDR_CMD BIT(3) /* Last host write was a command */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * wilco_ec_response_timed_out() - Wait for EC response. 498c2ecf20Sopenharmony_ci * @ec: EC device. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Return: true if EC timed out, false if EC did not time out. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic bool wilco_ec_response_timed_out(struct wilco_ec_device *ec) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci do { 588c2ecf20Sopenharmony_ci if (!(inb(ec->io_command->start) & 598c2ecf20Sopenharmony_ci (EC_CMDR_PENDING | EC_CMDR_BUSY))) 608c2ecf20Sopenharmony_ci return false; 618c2ecf20Sopenharmony_ci usleep_range(100, 200); 628c2ecf20Sopenharmony_ci } while (time_before(jiffies, timeout)); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return true; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/** 688c2ecf20Sopenharmony_ci * wilco_ec_checksum() - Compute 8-bit checksum over data range. 698c2ecf20Sopenharmony_ci * @data: Data to checksum. 708c2ecf20Sopenharmony_ci * @size: Number of bytes to checksum. 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * Return: 8-bit checksum of provided data. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic u8 wilco_ec_checksum(const void *data, size_t size) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u8 *data_bytes = (u8 *)data; 778c2ecf20Sopenharmony_ci u8 checksum = 0; 788c2ecf20Sopenharmony_ci size_t i; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) 818c2ecf20Sopenharmony_ci checksum += data_bytes[i]; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return checksum; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/** 878c2ecf20Sopenharmony_ci * wilco_ec_prepare() - Prepare the request structure for the EC. 888c2ecf20Sopenharmony_ci * @msg: EC message with request information. 898c2ecf20Sopenharmony_ci * @rq: EC request structure to fill. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic void wilco_ec_prepare(struct wilco_ec_message *msg, 928c2ecf20Sopenharmony_ci struct wilco_ec_request *rq) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci memset(rq, 0, sizeof(*rq)); 958c2ecf20Sopenharmony_ci rq->struct_version = EC_MAILBOX_PROTO_VERSION; 968c2ecf20Sopenharmony_ci rq->mailbox_id = msg->type; 978c2ecf20Sopenharmony_ci rq->mailbox_version = EC_MAILBOX_VERSION; 988c2ecf20Sopenharmony_ci rq->data_size = msg->request_size; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Checksum header and data */ 1018c2ecf20Sopenharmony_ci rq->checksum = wilco_ec_checksum(rq, sizeof(*rq)); 1028c2ecf20Sopenharmony_ci rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size); 1038c2ecf20Sopenharmony_ci rq->checksum = -rq->checksum; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/** 1078c2ecf20Sopenharmony_ci * wilco_ec_transfer() - Perform actual data transfer. 1088c2ecf20Sopenharmony_ci * @ec: EC device. 1098c2ecf20Sopenharmony_ci * @msg: EC message data for request and response. 1108c2ecf20Sopenharmony_ci * @rq: Filled in request structure 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * Context: ec->mailbox_lock should be held while using this function. 1138c2ecf20Sopenharmony_ci * Return: number of bytes received or negative error code on failure. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic int wilco_ec_transfer(struct wilco_ec_device *ec, 1168c2ecf20Sopenharmony_ci struct wilco_ec_message *msg, 1178c2ecf20Sopenharmony_ci struct wilco_ec_request *rq) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct wilco_ec_response *rs; 1208c2ecf20Sopenharmony_ci u8 checksum; 1218c2ecf20Sopenharmony_ci u8 flag; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Write request header, then data */ 1248c2ecf20Sopenharmony_ci cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq); 1258c2ecf20Sopenharmony_ci cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size, 1268c2ecf20Sopenharmony_ci msg->request_data); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Start the command */ 1298c2ecf20Sopenharmony_ci outb(EC_MAILBOX_START_COMMAND, ec->io_command->start); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* For some commands (eg shutdown) the EC will not respond, that's OK */ 1328c2ecf20Sopenharmony_ci if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) { 1338c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "EC does not respond to this command\n"); 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Wait for it to complete */ 1388c2ecf20Sopenharmony_ci if (wilco_ec_response_timed_out(ec)) { 1398c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "response timed out\n"); 1408c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Check result */ 1448c2ecf20Sopenharmony_ci flag = inb(ec->io_data->start); 1458c2ecf20Sopenharmony_ci if (flag) { 1468c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "bad response: 0x%02x\n", flag); 1478c2ecf20Sopenharmony_ci return -EIO; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Read back response */ 1518c2ecf20Sopenharmony_ci rs = ec->data_buffer; 1528c2ecf20Sopenharmony_ci checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0, 1538c2ecf20Sopenharmony_ci sizeof(*rs) + EC_MAILBOX_DATA_SIZE, 1548c2ecf20Sopenharmony_ci (u8 *)rs); 1558c2ecf20Sopenharmony_ci if (checksum) { 1568c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum); 1578c2ecf20Sopenharmony_ci return -EBADMSG; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (rs->result) { 1618c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "EC reported failure: 0x%02x\n", rs->result); 1628c2ecf20Sopenharmony_ci return -EBADMSG; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (rs->data_size != EC_MAILBOX_DATA_SIZE) { 1668c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "unexpected packet size (%u != %u)\n", 1678c2ecf20Sopenharmony_ci rs->data_size, EC_MAILBOX_DATA_SIZE); 1688c2ecf20Sopenharmony_ci return -EMSGSIZE; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (rs->data_size < msg->response_size) { 1728c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "EC didn't return enough data (%u < %zu)\n", 1738c2ecf20Sopenharmony_ci rs->data_size, msg->response_size); 1748c2ecf20Sopenharmony_ci return -EMSGSIZE; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci memcpy(msg->response_data, rs->data, msg->response_size); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return rs->data_size; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * wilco_ec_mailbox() - Send EC request and receive EC response. 1848c2ecf20Sopenharmony_ci * @ec: EC device. 1858c2ecf20Sopenharmony_ci * @msg: EC message data for request and response. 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * On entry msg->type, msg->request_size, and msg->request_data should all be 1888c2ecf20Sopenharmony_ci * filled in. If desired, msg->flags can be set. 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * If a response is expected, msg->response_size should be set, and 1918c2ecf20Sopenharmony_ci * msg->response_data should point to a buffer with enough space. On exit 1928c2ecf20Sopenharmony_ci * msg->response_data will be filled. 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Return: number of bytes received or negative error code on failure. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ciint wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct wilco_ec_request *rq; 1998c2ecf20Sopenharmony_ci int ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci dev_dbg(ec->dev, "type=%04x flags=%02x rslen=%zu rqlen=%zu\n", 2028c2ecf20Sopenharmony_ci msg->type, msg->flags, msg->response_size, msg->request_size); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci mutex_lock(&ec->mailbox_lock); 2058c2ecf20Sopenharmony_ci /* Prepare request packet */ 2068c2ecf20Sopenharmony_ci rq = ec->data_buffer; 2078c2ecf20Sopenharmony_ci wilco_ec_prepare(msg, rq); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ret = wilco_ec_transfer(ec, msg, rq); 2108c2ecf20Sopenharmony_ci mutex_unlock(&ec->mailbox_lock); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wilco_ec_mailbox); 216