Skip to content

Commit 38c1b38

Browse files
authored
Merge pull request #5 from DHowett/hx20
ectool: add support for the MEC LPC protocol
2 parents e4ba618 + 8494baf commit 38c1b38

File tree

3 files changed

+239
-2
lines changed

3 files changed

+239
-2
lines changed

util/build.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ include util/private/build.mk
3232
endif
3333

3434
comm-objs=$(util-lock-objs:%=lock/%) comm-host.o comm-dev.o
35-
comm-objs+=comm-lpc.o comm-i2c.o misc_util.o
35+
comm-objs+=comm-lpc.o comm-mec_lpc.o comm-i2c.o misc_util.o
3636

3737
iteflash-objs = iteflash.o usb_if.o
3838
ectool-objs=ectool.o ectool_keyscan.o ec_flash.o ec_panicinfo.o $(comm-objs)

util/comm-lpc.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* Wait for the EC to be unbusy. Returns 0 if unbusy, non-zero if
2222
* timeout.
2323
*/
24-
static int wait_for_ec(int status_addr, int timeout_usec)
24+
int wait_for_ec(int status_addr, int timeout_usec)
2525
{
2626
int i;
2727
int delay = INITIAL_UDELAY;
@@ -246,6 +246,8 @@ static int ec_readmem_lpc(int offset, int bytes, void *dest)
246246
return cnt;
247247
}
248248

249+
int comm_init_lpc_mec(void) __attribute__((weak));
250+
249251
int comm_init_lpc(void)
250252
{
251253
int i;
@@ -273,6 +275,15 @@ int comm_init_lpc(void)
273275
return -4;
274276
}
275277

278+
/* Check for a MEC first. */
279+
if (comm_init_lpc_mec && comm_init_lpc_mec() >= 0) {
280+
ec_max_outsize = EC_LPC_HOST_PACKET_SIZE -
281+
sizeof(struct ec_host_request);
282+
ec_max_insize = EC_LPC_HOST_PACKET_SIZE -
283+
sizeof(struct ec_host_response);
284+
return 0;
285+
}
286+
276287
/*
277288
* Test if LPC command args are supported.
278289
*

util/comm-mec_lpc.c

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/* Copyright 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+
/* The I/O asm funcs exist only on x86. */
7+
#if defined(__i386__) || defined(__x86_64__)
8+
9+
#include <stdint.h>
10+
#include <stdio.h>
11+
#include <sys/io.h>
12+
#include <sys/param.h>
13+
#include <unistd.h>
14+
#include <string.h>
15+
16+
#include "comm-host.h"
17+
18+
/* from comm-lpc.c */
19+
int wait_for_ec(int status_addr, int timeout_usec);
20+
21+
typedef enum _ec_xfer_direction { EC_MEC_WRITE, EC_MEC_READ } ec_xfer_direction;
22+
23+
/* As defined in MEC172x section 16.8.3
24+
* https://ww1.microchip.com/downloads/en/DeviceDoc/MEC172x-Data-Sheet-DS00003583C.pdf
25+
*/
26+
#define MEC_EC_BYTE_ACCESS 0x00
27+
#define MEC_EC_LONG_ACCESS_AUTOINCREMENT 0x03
28+
29+
#define MEC_EC_ADDRESS_REGISTER0 0x0802
30+
#define MEC_EC_ADDRESS_REGISTER1 0x0803
31+
#define MEC_EC_DATA_REGISTER0 0x0804
32+
#define MEC_EC_DATA_REGISTER2 0x0806
33+
#define MEC_EC_MEMMAP_START 0x100
34+
35+
static int ec_mec_xfer(ec_xfer_direction direction, uint16_t address,
36+
char *data, uint16_t size)
37+
{
38+
/*
39+
* There's a cleverer way to do this, but it's somewhat less clear what's happening.
40+
* I prefer clarity over cleverness. :)
41+
*/
42+
int pos = 0;
43+
uint16_t temp[2];
44+
if (address % 4 > 0) {
45+
outw((address & 0xFFFC) | MEC_EC_BYTE_ACCESS, MEC_EC_ADDRESS_REGISTER0);
46+
/* Unaligned start address */
47+
for (int i = address % 4; i < 4; ++i) {
48+
char *storage = &data[pos++];
49+
if (direction == EC_MEC_WRITE)
50+
outb(*storage, MEC_EC_DATA_REGISTER0 + i);
51+
else if (direction == EC_MEC_READ)
52+
*storage = inb(MEC_EC_DATA_REGISTER0 + i);
53+
}
54+
address = (address + 4) & 0xFFFC;
55+
}
56+
57+
if (size - pos >= 4) {
58+
outw((address & 0xFFFC) | MEC_EC_LONG_ACCESS_AUTOINCREMENT, MEC_EC_ADDRESS_REGISTER0);
59+
while (size - pos >= 4) {
60+
if (direction == EC_MEC_WRITE) {
61+
memcpy(temp, &data[pos], sizeof(temp));
62+
outw(temp[0], MEC_EC_DATA_REGISTER0);
63+
outw(temp[1], MEC_EC_DATA_REGISTER2);
64+
} else if (direction == EC_MEC_READ) {
65+
temp[0] = inw(MEC_EC_DATA_REGISTER0);
66+
temp[1] = inw(MEC_EC_DATA_REGISTER2);
67+
memcpy(&data[pos], temp, sizeof(temp));
68+
}
69+
70+
pos += 4;
71+
address += 4;
72+
}
73+
}
74+
75+
if (size - pos > 0) {
76+
outw((address & 0xFFFC) | MEC_EC_BYTE_ACCESS, MEC_EC_ADDRESS_REGISTER0);
77+
for (int i = 0; i < (size - pos); ++i) {
78+
char *storage = &data[pos + i];
79+
if (direction == EC_MEC_WRITE)
80+
outb(*storage, MEC_EC_DATA_REGISTER0 + i);
81+
else if (direction == EC_MEC_READ)
82+
*storage = inb(MEC_EC_DATA_REGISTER0 + i);
83+
}
84+
}
85+
return 0;
86+
}
87+
88+
static uint8_t ec_checksum_buffer(char *data, size_t size)
89+
{
90+
uint8_t sum = 0;
91+
for (int i = 0; i < size; ++i) {
92+
sum += data[i];
93+
}
94+
return sum;
95+
};
96+
97+
static int ec_command_lpc_mec_3(int command, int version, const void *outdata,
98+
int outsize, void *indata, int insize)
99+
{
100+
uint8_t csum = 0;
101+
int i;
102+
103+
union {
104+
struct ec_host_request rq;
105+
char data[EC_LPC_HOST_PACKET_SIZE];
106+
} u;
107+
108+
union {
109+
struct ec_host_response rs;
110+
char data[EC_LPC_HOST_PACKET_SIZE];
111+
} r;
112+
113+
/* Fail if output size is too big */
114+
if (outsize + sizeof(u.rq) > EC_LPC_HOST_PACKET_SIZE)
115+
return -EC_RES_REQUEST_TRUNCATED;
116+
117+
/* Fill in request packet */
118+
/* TODO(crosbug.com/p/23825): This should be common to all protocols */
119+
u.rq.struct_version = EC_HOST_REQUEST_VERSION;
120+
u.rq.checksum = 0;
121+
u.rq.command = command;
122+
u.rq.command_version = version;
123+
u.rq.reserved = 0;
124+
u.rq.data_len = outsize;
125+
126+
memcpy(&u.data[sizeof(u.rq)], outdata, outsize);
127+
csum = ec_checksum_buffer(u.data, outsize + sizeof(u.rq));
128+
u.rq.checksum = (uint8_t)(-csum);
129+
130+
if (wait_for_ec(EC_LPC_ADDR_HOST_CMD, 1000000)) {
131+
fprintf(stderr, "Timeout waiting for EC response\n");
132+
return -EC_RES_ERROR;
133+
}
134+
135+
ec_mec_xfer(EC_MEC_WRITE, 0, u.data, outsize + sizeof(u.rq));
136+
137+
/* Start the command */
138+
outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
139+
140+
if (wait_for_ec(EC_LPC_ADDR_HOST_CMD, 1000000)) {
141+
fprintf(stderr, "Timeout waiting for EC response\n");
142+
return -EC_RES_ERROR;
143+
}
144+
145+
/* Check result */
146+
i = inb(EC_LPC_ADDR_HOST_DATA);
147+
if (i) {
148+
fprintf(stderr, "EC returned error result code %d\n", i);
149+
return -EECRESULT - i;
150+
}
151+
152+
csum = 0;
153+
ec_mec_xfer(EC_MEC_READ, 0, r.data, sizeof(r.rs));
154+
155+
if (r.rs.struct_version != EC_HOST_RESPONSE_VERSION) {
156+
fprintf(stderr, "EC response version mismatch\n");
157+
return -EC_RES_INVALID_RESPONSE;
158+
}
159+
160+
if (r.rs.reserved) {
161+
fprintf(stderr, "EC response reserved != 0\n");
162+
return -EC_RES_INVALID_RESPONSE;
163+
}
164+
165+
if (r.rs.data_len > insize) {
166+
fprintf(stderr, "EC returned too much data\n");
167+
return -EC_RES_RESPONSE_TOO_BIG;
168+
}
169+
170+
if (r.rs.data_len > 0) {
171+
ec_mec_xfer(EC_MEC_READ, 8, r.data + sizeof(r.rs), r.rs.data_len);
172+
if (ec_checksum_buffer(r.data, sizeof(r.rs) + r.rs.data_len)) {
173+
fprintf(stderr, "EC response has invalid checksum\n");
174+
return -EC_RES_INVALID_CHECKSUM;
175+
}
176+
177+
memcpy(indata, r.data + sizeof(r.rs), r.rs.data_len);
178+
}
179+
return r.rs.data_len;
180+
}
181+
182+
static int ec_readmem_lpc_mec(int offset, int bytes, void *dest)
183+
{
184+
int i = offset;
185+
int cnt = 0;
186+
char* s = dest;
187+
188+
if (offset >= EC_MEMMAP_SIZE - bytes)
189+
return -1;
190+
191+
if (bytes) {
192+
ec_mec_xfer(EC_MEC_READ, MEC_EC_MEMMAP_START + i, dest, bytes);
193+
cnt = bytes;
194+
} else {
195+
/* Somewhat brute-force to set up a bunch of
196+
* individual transfers, but clearer than copying the xfer code
197+
* to add a stop condition.
198+
*/
199+
for(; i < EC_MEMMAP_SIZE; ++i, ++s) {
200+
ec_mec_xfer(EC_MEC_READ, MEC_EC_MEMMAP_START + i, s, 1);
201+
cnt++;
202+
if (!*s)
203+
break;
204+
}
205+
}
206+
return cnt;
207+
}
208+
209+
int comm_init_lpc_mec(void)
210+
{
211+
char signature[2];
212+
213+
/* This function assumes some setup was done by comm_init_lpc. */
214+
215+
ec_readmem_lpc_mec(EC_MEMMAP_ID, 2, &signature[0]);
216+
if (signature[0] != 'E' || signature[1] != 'C') {
217+
return -1;
218+
}
219+
220+
ec_command_proto = ec_command_lpc_mec_3;
221+
ec_readmem = ec_readmem_lpc_mec;
222+
223+
return 0;
224+
}
225+
226+
#endif

0 commit comments

Comments
 (0)