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