xref: /openbmc/u-boot/drivers/misc/cros_ec_lpc.c (revision 983c72f4)
1 /*
2  * Chromium OS cros_ec driver - LPC interface
3  *
4  * Copyright (c) 2012 The Chromium OS Authors.
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23 
24 /*
25  * The Matrix Keyboard Protocol driver handles talking to the keyboard
26  * controller chip. Mostly this is for keyboard functions, but some other
27  * things have slipped in, so we provide generic services to talk to the
28  * KBC.
29  */
30 
31 #include <common.h>
32 #include <command.h>
33 #include <cros_ec.h>
34 #include <asm/io.h>
35 
36 #ifdef DEBUG_TRACE
37 #define debug_trace(fmt, b...)	debug(fmt, ##b)
38 #else
39 #define debug_trace(fmt, b...)
40 #endif
41 
42 static int wait_for_sync(struct cros_ec_dev *dev)
43 {
44 	unsigned long start;
45 
46 	start = get_timer(0);
47 	while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) {
48 		if (get_timer(start) > 1000) {
49 			debug("%s: Timeout waiting for CROS_EC sync\n",
50 			      __func__);
51 			return -1;
52 		}
53 	}
54 
55 	return 0;
56 }
57 
58 /**
59  * Send a command to a LPC CROS_EC device and return the reply.
60  *
61  * The device's internal input/output buffers are used.
62  *
63  * @param dev		CROS_EC device
64  * @param cmd		Command to send (EC_CMD_...)
65  * @param cmd_version	Version of command to send (EC_VER_...)
66  * @param dout          Output data (may be NULL If dout_len=0)
67  * @param dout_len      Size of output data in bytes
68  * @param dinp          Place to put pointer to response data
69  * @param din_len       Maximum size of response in bytes
70  * @return number of bytes in response, or -1 on error
71  */
72 static int old_lpc_command(struct cros_ec_dev *dev, uint8_t cmd,
73 		     const uint8_t *dout, int dout_len,
74 		     uint8_t **dinp, int din_len)
75 {
76 	int ret, i;
77 
78 	if (dout_len > EC_OLD_PARAM_SIZE) {
79 		debug("%s: Cannot send %d bytes\n", __func__, dout_len);
80 		return -1;
81 	}
82 
83 	if (din_len > EC_OLD_PARAM_SIZE) {
84 		debug("%s: Cannot receive %d bytes\n", __func__, din_len);
85 		return -1;
86 	}
87 
88 	if (wait_for_sync(dev)) {
89 		debug("%s: Timeout waiting ready\n", __func__);
90 		return -1;
91 	}
92 
93 	debug_trace("cmd: %02x, ", cmd);
94 	for (i = 0; i < dout_len; i++) {
95 		debug_trace("%02x ", dout[i]);
96 		outb(dout[i], EC_LPC_ADDR_OLD_PARAM + i);
97 	}
98 	outb(cmd, EC_LPC_ADDR_HOST_CMD);
99 	debug_trace("\n");
100 
101 	if (wait_for_sync(dev)) {
102 		debug("%s: Timeout waiting ready\n", __func__);
103 		return -1;
104 	}
105 
106 	ret = inb(EC_LPC_ADDR_HOST_DATA);
107 	if (ret) {
108 		debug("%s: CROS_EC result code %d\n", __func__, ret);
109 		return -ret;
110 	}
111 
112 	debug_trace("resp: %02x, ", ret);
113 	for (i = 0; i < din_len; i++) {
114 		dev->din[i] = inb(EC_LPC_ADDR_OLD_PARAM + i);
115 		debug_trace("%02x ", dev->din[i]);
116 	}
117 	debug_trace("\n");
118 	*dinp = dev->din;
119 
120 	return din_len;
121 }
122 
123 int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,
124 		     const uint8_t *dout, int dout_len,
125 		     uint8_t **dinp, int din_len)
126 {
127 	const int cmd_addr = EC_LPC_ADDR_HOST_CMD;
128 	const int data_addr = EC_LPC_ADDR_HOST_DATA;
129 	const int args_addr = EC_LPC_ADDR_HOST_ARGS;
130 	const int param_addr = EC_LPC_ADDR_HOST_PARAM;
131 
132 	struct ec_lpc_host_args args;
133 	uint8_t *d;
134 	int csum;
135 	int i;
136 
137 	/* Fall back to old-style command interface if args aren't supported */
138 	if (!dev->cmd_version_is_supported)
139 		return old_lpc_command(dev, cmd, dout, dout_len, dinp,
140 				       din_len);
141 
142 	if (dout_len > EC_HOST_PARAM_SIZE) {
143 		debug("%s: Cannot send %d bytes\n", __func__, dout_len);
144 		return -1;
145 	}
146 
147 	/* Fill in args */
148 	args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
149 	args.command_version = cmd_version;
150 	args.data_size = dout_len;
151 
152 	/* Calculate checksum */
153 	csum = cmd + args.flags + args.command_version + args.data_size;
154 	for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++)
155 		csum += *d;
156 
157 	args.checksum = (uint8_t)csum;
158 
159 	if (wait_for_sync(dev)) {
160 		debug("%s: Timeout waiting ready\n", __func__);
161 		return -1;
162 	}
163 
164 	/* Write args */
165 	for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
166 		outb(*d, args_addr + i);
167 
168 	/* Write data, if any */
169 	debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version);
170 	for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) {
171 		outb(*d, param_addr + i);
172 		debug_trace("%02x ", *d);
173 	}
174 
175 	outb(cmd, cmd_addr);
176 	debug_trace("\n");
177 
178 	if (wait_for_sync(dev)) {
179 		debug("%s: Timeout waiting for response\n", __func__);
180 		return -1;
181 	}
182 
183 	/* Check result */
184 	i = inb(data_addr);
185 	if (i) {
186 		debug("%s: CROS_EC result code %d\n", __func__, i);
187 		return -i;
188 	}
189 
190 	/* Read back args */
191 	for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
192 		*d = inb(args_addr + i);
193 
194 	/*
195 	 * If EC didn't modify args flags, then somehow we sent a new-style
196 	 * command to an old EC, which means it would have read its params
197 	 * from the wrong place.
198 	 */
199 	if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) {
200 		debug("%s: CROS_EC protocol mismatch\n", __func__);
201 		return -EC_RES_INVALID_RESPONSE;
202 	}
203 
204 	if (args.data_size > din_len) {
205 		debug("%s: CROS_EC returned too much data %d > %d\n",
206 		      __func__, args.data_size, din_len);
207 		return -EC_RES_INVALID_RESPONSE;
208 	}
209 
210 	/* Read data, if any */
211 	for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) {
212 		*d = inb(param_addr + i);
213 		debug_trace("%02x ", *d);
214 	}
215 	debug_trace("\n");
216 
217 	/* Verify checksum */
218 	csum = cmd + args.flags + args.command_version + args.data_size;
219 	for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++)
220 		csum += *d;
221 
222 	if (args.checksum != (uint8_t)csum) {
223 		debug("%s: CROS_EC response has invalid checksum\n", __func__);
224 		return -EC_RES_INVALID_CHECKSUM;
225 	}
226 	*dinp = dev->din;
227 
228 	/* Return actual amount of data received */
229 	return args.data_size;
230 }
231 
232 /**
233  * Initialize LPC protocol.
234  *
235  * @param dev		CROS_EC device
236  * @param blob		Device tree blob
237  * @return 0 if ok, -1 on error
238  */
239 int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob)
240 {
241 	int byte, i;
242 
243 	/* See if we can find an EC at the other end */
244 	byte = 0xff;
245 	byte &= inb(EC_LPC_ADDR_HOST_CMD);
246 	byte &= inb(EC_LPC_ADDR_HOST_DATA);
247 	for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++)
248 		byte &= inb(EC_LPC_ADDR_HOST_PARAM + i);
249 	if (byte == 0xff) {
250 		debug("%s: CROS_EC device not found on LPC bus\n",
251 			__func__);
252 		return -1;
253 	}
254 
255 	return 0;
256 }
257 
258 /*
259  * Test if LPC command args are supported.
260  *
261  * The cheapest way to do this is by looking for the memory-mapped
262  * flag.  This is faster than sending a new-style 'hello' command and
263  * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
264  * in args when it responds.
265  */
266 int cros_ec_lpc_check_version(struct cros_ec_dev *dev)
267 {
268 	if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' &&
269 			inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1)
270 				== 'C' &&
271 			(inb(EC_LPC_ADDR_MEMMAP +
272 				EC_MEMMAP_HOST_CMD_FLAGS) &
273 				EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) {
274 		dev->cmd_version_is_supported = 1;
275 	} else {
276 		/* We are going to use the old IO ports */
277 		dev->cmd_version_is_supported = 0;
278 	}
279 	debug("lpc: version %s\n", dev->cmd_version_is_supported ?
280 			"new" : "old");
281 
282 	return 0;
283 }
284