1/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6#include <chromiumos-platform-ec/ec_commands.h>
7
8#include <stdint.h>
9#include <stdio.h>
10#include <sys/io.h>
11#include <sys/param.h>
12#include <unistd.h>
13#include <zircon/errors.h>
14
15#define INITIAL_UDELAY 5     /* 5 us */
16#define MAXIMUM_UDELAY 10000 /* 10 ms */
17
18/*
19 * Wait for the EC to be unbusy.  Returns 0 if unbusy, non-zero if
20 * timeout.
21 */
22static int wait_for_ec(uint16_t status_addr, int timeout_usec)
23{
24	int i;
25	int delay = INITIAL_UDELAY;
26
27	for (i = 0; i < timeout_usec; i += delay) {
28		/*
29		 * Delay first, in case we just sent out a command but the EC
30		 * hasn't raised the busy flag.  However, I think this doesn't
31		 * happen since the LPC commands are executed in order and the
32		 * busy flag is set by hardware.  Minor issue in any case,
33		 * since the initial delay is very short.
34		 */
35		usleep(MIN(delay, timeout_usec - i));
36
37		if (!(inb(status_addr) & EC_LPC_STATUS_BUSY_MASK))
38			return 0;
39
40		/* Increase the delay interval after a few rapid checks */
41		if (i > 20)
42			delay = MIN(delay * 2, MAXIMUM_UDELAY);
43	}
44	return -1;  /* Timeout */
45}
46
47namespace CrOsEc {
48
49zx_status_t CommandLpc3(uint16_t command, uint8_t version,
50					   const void *outdata, size_t outsize,
51					   void *indata, size_t insize, size_t *actual)
52{
53	struct ec_host_request rq;
54	struct ec_host_response rs;
55	const uint8_t *d;
56	uint8_t *dout;
57	int csum = 0;
58	size_t i;
59
60	static_assert(EC_LPC_HOST_PACKET_SIZE <= UINT16_MAX, "");
61	/* Fail if output size is too big */
62	if (outsize + sizeof(rq) > EC_LPC_HOST_PACKET_SIZE)
63		return ZX_ERR_INVALID_ARGS;
64
65	/* Fill in request packet */
66	/* TODO(crosbug.com/p/23825): This should be common to all protocols */
67	rq.struct_version = EC_HOST_REQUEST_VERSION;
68	rq.checksum = 0;
69	rq.command = command;
70	rq.command_version = version;
71	rq.reserved = 0;
72	rq.data_len = static_cast<uint16_t>(outsize);
73
74	/* Copy data and start checksum */
75	for (i = 0, d = (const uint8_t *)outdata; i < outsize; i++, d++) {
76		const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + sizeof(rq) + i);
77		outb(*d, addr);
78		csum += *d;
79	}
80
81	/* Finish checksum */
82	for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++)
83		csum += *d;
84
85	/* Write checksum field so the entire packet sums to 0 */
86	rq.checksum = (uint8_t)(-csum);
87
88	/* Copy header */
89	for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++) {
90		const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + i);
91		outb(*d, addr);
92	}
93
94	/* Start the command */
95	outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
96
97	if (wait_for_ec(EC_LPC_ADDR_HOST_CMD, 1000000)) {
98		return ZX_ERR_TIMED_OUT;
99	}
100
101	/* Check result */
102	i = inb(EC_LPC_ADDR_HOST_DATA);
103	if (i) {
104		return ZX_ERR_IO;
105	}
106
107	/* Read back response header and start checksum */
108	csum = 0;
109	for (i = 0, dout = (uint8_t *)&rs; i < sizeof(rs); i++, dout++) {
110		const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + i);
111		*dout = inb(addr);
112		csum += *dout;
113	}
114
115	if (rs.struct_version != EC_HOST_RESPONSE_VERSION) {
116		return ZX_ERR_IO;
117	}
118
119	if (rs.reserved) {
120		return ZX_ERR_IO;
121	}
122
123	if (rs.data_len > insize) {
124		return ZX_ERR_BUFFER_TOO_SMALL;
125	}
126
127	/* Read back data and update checksum */
128	for (i = 0, dout = (uint8_t *)indata; i < rs.data_len; i++, dout++) {
129		const uint16_t addr = static_cast<uint16_t>(EC_LPC_ADDR_HOST_PACKET + sizeof(rs) + i);
130		*dout = inb(addr);
131		csum += *dout;
132	}
133
134	/* Verify checksum */
135	if ((uint8_t)csum) {
136		return ZX_ERR_IO_DATA_INTEGRITY;
137	}
138
139	/* Return actual amount of data received */
140	*actual = rs.data_len;
141	return ZX_OK;
142}
143
144bool IsLpc3Supported()
145{
146	int i;
147	int byte = 0xff;
148
149	/*
150	 * Test if the I/O port has been configured for Chromium EC LPC
151	 * interface.  Chromium EC guarantees that at least one status bit will
152	 * be 0, so if the command and data bytes are both 0xff, very likely
153	 * that Chromium EC is not present.  See crosbug.com/p/10963.
154	 */
155	byte &= inb(EC_LPC_ADDR_HOST_CMD);
156	byte &= inb(EC_LPC_ADDR_HOST_DATA);
157	if (byte == 0xff) {
158		return false;
159	}
160
161	/*
162	 * Test if LPC command args are supported.
163	 *
164	 * The cheapest way to do this is by looking for the memory-mapped
165	 * flag.  This is faster than sending a new-style 'hello' command and
166	 * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
167	 * in args when it responds.
168	 */
169	if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E' ||
170	    inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C') {
171		return false;
172	}
173
174	/* Check which command version we'll use */
175	i = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS);
176	return !!(i & EC_HOST_CMD_FLAG_VERSION_3);
177}
178
179} // namespace CrOsEc
180