162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Mailbox interface for Wilco Embedded Controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2018 Google LLC 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The Wilco EC is similar to a typical ChromeOS embedded controller. 862306a36Sopenharmony_ci * It uses the same MEC based low-level communication and a similar 962306a36Sopenharmony_ci * protocol, but with some important differences. The EC firmware does 1062306a36Sopenharmony_ci * not support the same mailbox commands so it is not registered as a 1162306a36Sopenharmony_ci * cros_ec device type. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Most messages follow a standard format, but there are some exceptions 1462306a36Sopenharmony_ci * and an interface is provided to do direct/raw transactions that do not 1562306a36Sopenharmony_ci * make assumptions about byte placement. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/device.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/platform_data/wilco-ec.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "../cros_ec_lpc_mec.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Version of mailbox interface */ 2762306a36Sopenharmony_ci#define EC_MAILBOX_VERSION 0 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Command to start mailbox transaction */ 3062306a36Sopenharmony_ci#define EC_MAILBOX_START_COMMAND 0xda 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Version of EC protocol */ 3362306a36Sopenharmony_ci#define EC_MAILBOX_PROTO_VERSION 3 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Number of header bytes to be counted as data bytes */ 3662306a36Sopenharmony_ci#define EC_MAILBOX_DATA_EXTRA 2 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Maximum timeout */ 3962306a36Sopenharmony_ci#define EC_MAILBOX_TIMEOUT HZ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* EC response flags */ 4262306a36Sopenharmony_ci#define EC_CMDR_DATA BIT(0) /* Data ready for host to read */ 4362306a36Sopenharmony_ci#define EC_CMDR_PENDING BIT(1) /* Write pending to EC */ 4462306a36Sopenharmony_ci#define EC_CMDR_BUSY BIT(2) /* EC is busy processing a command */ 4562306a36Sopenharmony_ci#define EC_CMDR_CMD BIT(3) /* Last host write was a command */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * wilco_ec_response_timed_out() - Wait for EC response. 4962306a36Sopenharmony_ci * @ec: EC device. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Return: true if EC timed out, false if EC did not time out. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic bool wilco_ec_response_timed_out(struct wilco_ec_device *ec) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci do { 5862306a36Sopenharmony_ci if (!(inb(ec->io_command->start) & 5962306a36Sopenharmony_ci (EC_CMDR_PENDING | EC_CMDR_BUSY))) 6062306a36Sopenharmony_ci return false; 6162306a36Sopenharmony_ci usleep_range(100, 200); 6262306a36Sopenharmony_ci } while (time_before(jiffies, timeout)); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return true; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * wilco_ec_checksum() - Compute 8-bit checksum over data range. 6962306a36Sopenharmony_ci * @data: Data to checksum. 7062306a36Sopenharmony_ci * @size: Number of bytes to checksum. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Return: 8-bit checksum of provided data. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic u8 wilco_ec_checksum(const void *data, size_t size) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci u8 *data_bytes = (u8 *)data; 7762306a36Sopenharmony_ci u8 checksum = 0; 7862306a36Sopenharmony_ci size_t i; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci for (i = 0; i < size; i++) 8162306a36Sopenharmony_ci checksum += data_bytes[i]; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return checksum; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * wilco_ec_prepare() - Prepare the request structure for the EC. 8862306a36Sopenharmony_ci * @msg: EC message with request information. 8962306a36Sopenharmony_ci * @rq: EC request structure to fill. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic void wilco_ec_prepare(struct wilco_ec_message *msg, 9262306a36Sopenharmony_ci struct wilco_ec_request *rq) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci memset(rq, 0, sizeof(*rq)); 9562306a36Sopenharmony_ci rq->struct_version = EC_MAILBOX_PROTO_VERSION; 9662306a36Sopenharmony_ci rq->mailbox_id = msg->type; 9762306a36Sopenharmony_ci rq->mailbox_version = EC_MAILBOX_VERSION; 9862306a36Sopenharmony_ci rq->data_size = msg->request_size; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Checksum header and data */ 10162306a36Sopenharmony_ci rq->checksum = wilco_ec_checksum(rq, sizeof(*rq)); 10262306a36Sopenharmony_ci rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size); 10362306a36Sopenharmony_ci rq->checksum = -rq->checksum; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/** 10762306a36Sopenharmony_ci * wilco_ec_transfer() - Perform actual data transfer. 10862306a36Sopenharmony_ci * @ec: EC device. 10962306a36Sopenharmony_ci * @msg: EC message data for request and response. 11062306a36Sopenharmony_ci * @rq: Filled in request structure 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * Context: ec->mailbox_lock should be held while using this function. 11362306a36Sopenharmony_ci * Return: number of bytes received or negative error code on failure. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic int wilco_ec_transfer(struct wilco_ec_device *ec, 11662306a36Sopenharmony_ci struct wilco_ec_message *msg, 11762306a36Sopenharmony_ci struct wilco_ec_request *rq) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct wilco_ec_response *rs; 12062306a36Sopenharmony_ci u8 checksum; 12162306a36Sopenharmony_ci u8 flag; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Write request header, then data */ 12462306a36Sopenharmony_ci cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq); 12562306a36Sopenharmony_ci cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size, 12662306a36Sopenharmony_ci msg->request_data); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Start the command */ 12962306a36Sopenharmony_ci outb(EC_MAILBOX_START_COMMAND, ec->io_command->start); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* For some commands (eg shutdown) the EC will not respond, that's OK */ 13262306a36Sopenharmony_ci if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) { 13362306a36Sopenharmony_ci dev_dbg(ec->dev, "EC does not respond to this command\n"); 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Wait for it to complete */ 13862306a36Sopenharmony_ci if (wilco_ec_response_timed_out(ec)) { 13962306a36Sopenharmony_ci dev_dbg(ec->dev, "response timed out\n"); 14062306a36Sopenharmony_ci return -ETIMEDOUT; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Check result */ 14462306a36Sopenharmony_ci flag = inb(ec->io_data->start); 14562306a36Sopenharmony_ci if (flag) { 14662306a36Sopenharmony_ci dev_dbg(ec->dev, "bad response: 0x%02x\n", flag); 14762306a36Sopenharmony_ci return -EIO; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Read back response */ 15162306a36Sopenharmony_ci rs = ec->data_buffer; 15262306a36Sopenharmony_ci checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0, 15362306a36Sopenharmony_ci sizeof(*rs) + EC_MAILBOX_DATA_SIZE, 15462306a36Sopenharmony_ci (u8 *)rs); 15562306a36Sopenharmony_ci if (checksum) { 15662306a36Sopenharmony_ci dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum); 15762306a36Sopenharmony_ci return -EBADMSG; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (rs->result) { 16162306a36Sopenharmony_ci dev_dbg(ec->dev, "EC reported failure: 0x%02x\n", rs->result); 16262306a36Sopenharmony_ci return -EBADMSG; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (rs->data_size != EC_MAILBOX_DATA_SIZE) { 16662306a36Sopenharmony_ci dev_dbg(ec->dev, "unexpected packet size (%u != %u)\n", 16762306a36Sopenharmony_ci rs->data_size, EC_MAILBOX_DATA_SIZE); 16862306a36Sopenharmony_ci return -EMSGSIZE; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (rs->data_size < msg->response_size) { 17262306a36Sopenharmony_ci dev_dbg(ec->dev, "EC didn't return enough data (%u < %zu)\n", 17362306a36Sopenharmony_ci rs->data_size, msg->response_size); 17462306a36Sopenharmony_ci return -EMSGSIZE; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci memcpy(msg->response_data, rs->data, msg->response_size); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return rs->data_size; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/** 18362306a36Sopenharmony_ci * wilco_ec_mailbox() - Send EC request and receive EC response. 18462306a36Sopenharmony_ci * @ec: EC device. 18562306a36Sopenharmony_ci * @msg: EC message data for request and response. 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * On entry msg->type, msg->request_size, and msg->request_data should all be 18862306a36Sopenharmony_ci * filled in. If desired, msg->flags can be set. 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * If a response is expected, msg->response_size should be set, and 19162306a36Sopenharmony_ci * msg->response_data should point to a buffer with enough space. On exit 19262306a36Sopenharmony_ci * msg->response_data will be filled. 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Return: number of bytes received or negative error code on failure. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ciint wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct wilco_ec_request *rq; 19962306a36Sopenharmony_ci int ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci dev_dbg(ec->dev, "type=%04x flags=%02x rslen=%zu rqlen=%zu\n", 20262306a36Sopenharmony_ci msg->type, msg->flags, msg->response_size, msg->request_size); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci mutex_lock(&ec->mailbox_lock); 20562306a36Sopenharmony_ci /* Prepare request packet */ 20662306a36Sopenharmony_ci rq = ec->data_buffer; 20762306a36Sopenharmony_ci wilco_ec_prepare(msg, rq); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = wilco_ec_transfer(ec, msg, rq); 21062306a36Sopenharmony_ci mutex_unlock(&ec->mailbox_lock); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wilco_ec_mailbox); 216