2 * Chromium OS cros_ec driver - LPC interface
4 * Copyright (c) 2012 The Chromium OS Authors.
6 * SPDX-License-Identifier: GPL-2.0+
10 * The Matrix Keyboard Protocol driver handles talking to the keyboard
11 * controller chip. Mostly this is for keyboard functions, but some other
12 * things have slipped in, so we provide generic services to talk to the
22 #define debug_trace(fmt, b...) debug(fmt, ##b)
24 #define debug_trace(fmt, b...)
27 static int wait_for_sync(struct cros_ec_dev
*dev
)
32 while (inb(EC_LPC_ADDR_HOST_CMD
) & EC_LPC_STATUS_BUSY_MASK
) {
33 if (get_timer(start
) > 1000) {
34 debug("%s: Timeout waiting for CROS_EC sync\n",
44 * Send a command to a LPC CROS_EC device and return the reply.
46 * The device's internal input/output buffers are used.
48 * @param dev CROS_EC device
49 * @param cmd Command to send (EC_CMD_...)
50 * @param cmd_version Version of command to send (EC_VER_...)
51 * @param dout Output data (may be NULL If dout_len=0)
52 * @param dout_len Size of output data in bytes
53 * @param dinp Place to put pointer to response data
54 * @param din_len Maximum size of response in bytes
55 * @return number of bytes in response, or -1 on error
57 static int old_lpc_command(struct cros_ec_dev
*dev
, uint8_t cmd
,
58 const uint8_t *dout
, int dout_len
,
59 uint8_t **dinp
, int din_len
)
63 if (dout_len
> EC_OLD_PARAM_SIZE
) {
64 debug("%s: Cannot send %d bytes\n", __func__
, dout_len
);
68 if (din_len
> EC_OLD_PARAM_SIZE
) {
69 debug("%s: Cannot receive %d bytes\n", __func__
, din_len
);
73 if (wait_for_sync(dev
)) {
74 debug("%s: Timeout waiting ready\n", __func__
);
78 debug_trace("cmd: %02x, ", cmd
);
79 for (i
= 0; i
< dout_len
; i
++) {
80 debug_trace("%02x ", dout
[i
]);
81 outb(dout
[i
], EC_LPC_ADDR_OLD_PARAM
+ i
);
83 outb(cmd
, EC_LPC_ADDR_HOST_CMD
);
86 if (wait_for_sync(dev
)) {
87 debug("%s: Timeout waiting ready\n", __func__
);
91 ret
= inb(EC_LPC_ADDR_HOST_DATA
);
93 debug("%s: CROS_EC result code %d\n", __func__
, ret
);
97 debug_trace("resp: %02x, ", ret
);
98 for (i
= 0; i
< din_len
; i
++) {
99 dev
->din
[i
] = inb(EC_LPC_ADDR_OLD_PARAM
+ i
);
100 debug_trace("%02x ", dev
->din
[i
]);
108 int cros_ec_lpc_command(struct cros_ec_dev
*dev
, uint8_t cmd
, int cmd_version
,
109 const uint8_t *dout
, int dout_len
,
110 uint8_t **dinp
, int din_len
)
112 const int cmd_addr
= EC_LPC_ADDR_HOST_CMD
;
113 const int data_addr
= EC_LPC_ADDR_HOST_DATA
;
114 const int args_addr
= EC_LPC_ADDR_HOST_ARGS
;
115 const int param_addr
= EC_LPC_ADDR_HOST_PARAM
;
117 struct ec_lpc_host_args args
;
122 /* Fall back to old-style command interface if args aren't supported */
123 if (!dev
->cmd_version_is_supported
)
124 return old_lpc_command(dev
, cmd
, dout
, dout_len
, dinp
,
127 if (dout_len
> EC_HOST_PARAM_SIZE
) {
128 debug("%s: Cannot send %d bytes\n", __func__
, dout_len
);
133 args
.flags
= EC_HOST_ARGS_FLAG_FROM_HOST
;
134 args
.command_version
= cmd_version
;
135 args
.data_size
= dout_len
;
137 /* Calculate checksum */
138 csum
= cmd
+ args
.flags
+ args
.command_version
+ args
.data_size
;
139 for (i
= 0, d
= (uint8_t *)dout
; i
< dout_len
; i
++, d
++)
142 args
.checksum
= (uint8_t)csum
;
144 if (wait_for_sync(dev
)) {
145 debug("%s: Timeout waiting ready\n", __func__
);
150 for (i
= 0, d
= (uint8_t *)&args
; i
< sizeof(args
); i
++, d
++)
151 outb(*d
, args_addr
+ i
);
153 /* Write data, if any */
154 debug_trace("cmd: %02x, ver: %02x", cmd
, cmd_version
);
155 for (i
= 0, d
= (uint8_t *)dout
; i
< dout_len
; i
++, d
++) {
156 outb(*d
, param_addr
+ i
);
157 debug_trace("%02x ", *d
);
163 if (wait_for_sync(dev
)) {
164 debug("%s: Timeout waiting for response\n", __func__
);
171 debug("%s: CROS_EC result code %d\n", __func__
, i
);
176 for (i
= 0, d
= (uint8_t *)&args
; i
< sizeof(args
); i
++, d
++)
177 *d
= inb(args_addr
+ i
);
180 * If EC didn't modify args flags, then somehow we sent a new-style
181 * command to an old EC, which means it would have read its params
182 * from the wrong place.
184 if (!(args
.flags
& EC_HOST_ARGS_FLAG_TO_HOST
)) {
185 debug("%s: CROS_EC protocol mismatch\n", __func__
);
186 return -EC_RES_INVALID_RESPONSE
;
189 if (args
.data_size
> din_len
) {
190 debug("%s: CROS_EC returned too much data %d > %d\n",
191 __func__
, args
.data_size
, din_len
);
192 return -EC_RES_INVALID_RESPONSE
;
195 /* Read data, if any */
196 for (i
= 0, d
= (uint8_t *)dev
->din
; i
< args
.data_size
; i
++, d
++) {
197 *d
= inb(param_addr
+ i
);
198 debug_trace("%02x ", *d
);
202 /* Verify checksum */
203 csum
= cmd
+ args
.flags
+ args
.command_version
+ args
.data_size
;
204 for (i
= 0, d
= (uint8_t *)dev
->din
; i
< args
.data_size
; i
++, d
++)
207 if (args
.checksum
!= (uint8_t)csum
) {
208 debug("%s: CROS_EC response has invalid checksum\n", __func__
);
209 return -EC_RES_INVALID_CHECKSUM
;
213 /* Return actual amount of data received */
214 return args
.data_size
;
218 * Initialize LPC protocol.
220 * @param dev CROS_EC device
221 * @param blob Device tree blob
222 * @return 0 if ok, -1 on error
224 int cros_ec_lpc_init(struct cros_ec_dev
*dev
, const void *blob
)
228 /* See if we can find an EC at the other end */
230 byte
&= inb(EC_LPC_ADDR_HOST_CMD
);
231 byte
&= inb(EC_LPC_ADDR_HOST_DATA
);
232 for (i
= 0; i
< EC_HOST_PARAM_SIZE
&& (byte
== 0xff); i
++)
233 byte
&= inb(EC_LPC_ADDR_HOST_PARAM
+ i
);
235 debug("%s: CROS_EC device not found on LPC bus\n",
244 * Test if LPC command args are supported.
246 * The cheapest way to do this is by looking for the memory-mapped
247 * flag. This is faster than sending a new-style 'hello' command and
248 * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
249 * in args when it responds.
251 int cros_ec_lpc_check_version(struct cros_ec_dev
*dev
)
253 if (inb(EC_LPC_ADDR_MEMMAP
+ EC_MEMMAP_ID
) == 'E' &&
254 inb(EC_LPC_ADDR_MEMMAP
+ EC_MEMMAP_ID
+ 1)
256 (inb(EC_LPC_ADDR_MEMMAP
+
257 EC_MEMMAP_HOST_CMD_FLAGS
) &
258 EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED
)) {
259 dev
->cmd_version_is_supported
= 1;
261 /* We are going to use the old IO ports */
262 dev
->cmd_version_is_supported
= 0;
264 debug("lpc: version %s\n", dev
->cmd_version_is_supported
?