222 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Mailbox interface for Wilco Embedded Controller4 *5 * Copyright 2018 Google LLC6 *7 * The Wilco EC is similar to a typical ChromeOS embedded controller.8 * It uses the same MEC based low-level communication and a similar9 * protocol, but with some important differences. The EC firmware does10 * not support the same mailbox commands so it is not registered as a11 * cros_ec device type.12 *13 * Most messages follow a standard format, but there are some exceptions14 * and an interface is provided to do direct/raw transactions that do not15 * make assumptions about byte placement.16 */17 18#include <linux/delay.h>19#include <linux/device.h>20#include <linux/io.h>21#include <linux/platform_data/wilco-ec.h>22#include <linux/platform_device.h>23 24#include "../cros_ec_lpc_mec.h"25 26/* Version of mailbox interface */27#define EC_MAILBOX_VERSION 028 29/* Command to start mailbox transaction */30#define EC_MAILBOX_START_COMMAND 0xda31 32/* Version of EC protocol */33#define EC_MAILBOX_PROTO_VERSION 334 35/* Number of header bytes to be counted as data bytes */36#define EC_MAILBOX_DATA_EXTRA 237 38/* Maximum timeout */39#define EC_MAILBOX_TIMEOUT HZ40 41/* EC response flags */42#define EC_CMDR_DATA BIT(0) /* Data ready for host to read */43#define EC_CMDR_PENDING BIT(1) /* Write pending to EC */44#define EC_CMDR_BUSY BIT(2) /* EC is busy processing a command */45#define EC_CMDR_CMD BIT(3) /* Last host write was a command */46 47/**48 * wilco_ec_response_timed_out() - Wait for EC response.49 * @ec: EC device.50 *51 * Return: true if EC timed out, false if EC did not time out.52 */53static bool wilco_ec_response_timed_out(struct wilco_ec_device *ec)54{55 unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT;56 57 do {58 if (!(inb(ec->io_command->start) &59 (EC_CMDR_PENDING | EC_CMDR_BUSY)))60 return false;61 usleep_range(100, 200);62 } while (time_before(jiffies, timeout));63 64 return true;65}66 67/**68 * wilco_ec_checksum() - Compute 8-bit checksum over data range.69 * @data: Data to checksum.70 * @size: Number of bytes to checksum.71 *72 * Return: 8-bit checksum of provided data.73 */74static u8 wilco_ec_checksum(const void *data, size_t size)75{76 u8 *data_bytes = (u8 *)data;77 u8 checksum = 0;78 size_t i;79 80 for (i = 0; i < size; i++)81 checksum += data_bytes[i];82 83 return checksum;84}85 86/**87 * wilco_ec_prepare() - Prepare the request structure for the EC.88 * @msg: EC message with request information.89 * @rq: EC request structure to fill.90 */91static void wilco_ec_prepare(struct wilco_ec_message *msg,92 struct wilco_ec_request *rq)93{94 memset(rq, 0, sizeof(*rq));95 rq->struct_version = EC_MAILBOX_PROTO_VERSION;96 rq->mailbox_id = msg->type;97 rq->mailbox_version = EC_MAILBOX_VERSION;98 rq->data_size = msg->request_size;99 100 /* Checksum header and data */101 rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));102 rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size);103 rq->checksum = -rq->checksum;104}105 106/**107 * wilco_ec_transfer() - Perform actual data transfer.108 * @ec: EC device.109 * @msg: EC message data for request and response.110 * @rq: Filled in request structure111 *112 * Context: ec->mailbox_lock should be held while using this function.113 * Return: number of bytes received or negative error code on failure.114 */115static int wilco_ec_transfer(struct wilco_ec_device *ec,116 struct wilco_ec_message *msg,117 struct wilco_ec_request *rq)118{119 struct wilco_ec_response *rs;120 int ret;121 u8 flag;122 123 /* Write request header, then data */124 ret = cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);125 if (ret < 0)126 return ret;127 ret = cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,128 msg->request_data);129 if (ret < 0)130 return ret;131 132 /* Start the command */133 outb(EC_MAILBOX_START_COMMAND, ec->io_command->start);134 135 /* For some commands (eg shutdown) the EC will not respond, that's OK */136 if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) {137 dev_dbg(ec->dev, "EC does not respond to this command\n");138 return 0;139 }140 141 /* Wait for it to complete */142 if (wilco_ec_response_timed_out(ec)) {143 dev_dbg(ec->dev, "response timed out\n");144 return -ETIMEDOUT;145 }146 147 /* Check result */148 flag = inb(ec->io_data->start);149 if (flag) {150 dev_dbg(ec->dev, "bad response: 0x%02x\n", flag);151 return -EIO;152 }153 154 /* Read back response */155 rs = ec->data_buffer;156 ret = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,157 sizeof(*rs) + EC_MAILBOX_DATA_SIZE,158 (u8 *)rs);159 if (ret < 0)160 return ret;161 if (ret) {162 dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum);163 return -EBADMSG;164 }165 166 if (rs->result) {167 dev_dbg(ec->dev, "EC reported failure: 0x%02x\n", rs->result);168 return -EBADMSG;169 }170 171 if (rs->data_size != EC_MAILBOX_DATA_SIZE) {172 dev_dbg(ec->dev, "unexpected packet size (%u != %u)\n",173 rs->data_size, EC_MAILBOX_DATA_SIZE);174 return -EMSGSIZE;175 }176 177 if (rs->data_size < msg->response_size) {178 dev_dbg(ec->dev, "EC didn't return enough data (%u < %zu)\n",179 rs->data_size, msg->response_size);180 return -EMSGSIZE;181 }182 183 memcpy(msg->response_data, rs->data, msg->response_size);184 185 return rs->data_size;186}187 188/**189 * wilco_ec_mailbox() - Send EC request and receive EC response.190 * @ec: EC device.191 * @msg: EC message data for request and response.192 *193 * On entry msg->type, msg->request_size, and msg->request_data should all be194 * filled in. If desired, msg->flags can be set.195 *196 * If a response is expected, msg->response_size should be set, and197 * msg->response_data should point to a buffer with enough space. On exit198 * msg->response_data will be filled.199 *200 * Return: number of bytes received or negative error code on failure.201 */202int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg)203{204 struct wilco_ec_request *rq;205 int ret;206 207 dev_dbg(ec->dev, "type=%04x flags=%02x rslen=%zu rqlen=%zu\n",208 msg->type, msg->flags, msg->response_size, msg->request_size);209 210 mutex_lock(&ec->mailbox_lock);211 /* Prepare request packet */212 rq = ec->data_buffer;213 wilco_ec_prepare(msg, rq);214 215 ret = wilco_ec_transfer(ec, msg, rq);216 mutex_unlock(&ec->mailbox_lock);217 218 return ret;219 220}221EXPORT_SYMBOL_GPL(wilco_ec_mailbox);222