1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // ChromeOS EC communication protocol helper functions |
3 | // |
4 | // Copyright (C) 2015 Google, Inc |
5 | |
6 | #include <linux/delay.h> |
7 | #include <linux/device.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_data/cros_ec_commands.h> |
10 | #include <linux/platform_data/cros_ec_proto.h> |
11 | #include <linux/slab.h> |
12 | #include <asm/unaligned.h> |
13 | |
14 | #include "cros_ec_trace.h" |
15 | |
16 | #define EC_COMMAND_RETRIES 50 |
17 | |
18 | static const int cros_ec_error_map[] = { |
19 | [EC_RES_INVALID_COMMAND] = -EOPNOTSUPP, |
20 | [EC_RES_ERROR] = -EIO, |
21 | [EC_RES_INVALID_PARAM] = -EINVAL, |
22 | [EC_RES_ACCESS_DENIED] = -EACCES, |
23 | [EC_RES_INVALID_RESPONSE] = -EPROTO, |
24 | [EC_RES_INVALID_VERSION] = -ENOPROTOOPT, |
25 | [EC_RES_INVALID_CHECKSUM] = -EBADMSG, |
26 | [EC_RES_IN_PROGRESS] = -EINPROGRESS, |
27 | [EC_RES_UNAVAILABLE] = -ENODATA, |
28 | [EC_RES_TIMEOUT] = -ETIMEDOUT, |
29 | [EC_RES_OVERFLOW] = -EOVERFLOW, |
30 | [EC_RES_INVALID_HEADER] = -EBADR, |
31 | [EC_RES_REQUEST_TRUNCATED] = -EBADR, |
32 | [EC_RES_RESPONSE_TOO_BIG] = -EFBIG, |
33 | [EC_RES_BUS_ERROR] = -EFAULT, |
34 | [EC_RES_BUSY] = -EBUSY, |
35 | [EC_RES_INVALID_HEADER_VERSION] = -EBADMSG, |
36 | [EC_RES_INVALID_HEADER_CRC] = -EBADMSG, |
37 | [EC_RES_INVALID_DATA_CRC] = -EBADMSG, |
38 | [EC_RES_DUP_UNAVAILABLE] = -ENODATA, |
39 | }; |
40 | |
41 | static int cros_ec_map_error(uint32_t result) |
42 | { |
43 | int ret = 0; |
44 | |
45 | if (result != EC_RES_SUCCESS) { |
46 | if (result < ARRAY_SIZE(cros_ec_error_map) && cros_ec_error_map[result]) |
47 | ret = cros_ec_error_map[result]; |
48 | else |
49 | ret = -EPROTO; |
50 | } |
51 | |
52 | return ret; |
53 | } |
54 | |
55 | static int prepare_tx(struct cros_ec_device *ec_dev, |
56 | struct cros_ec_command *msg) |
57 | { |
58 | struct ec_host_request *request; |
59 | u8 *out; |
60 | int i; |
61 | u8 csum = 0; |
62 | |
63 | if (msg->outsize + sizeof(*request) > ec_dev->dout_size) |
64 | return -EINVAL; |
65 | |
66 | out = ec_dev->dout; |
67 | request = (struct ec_host_request *)out; |
68 | request->struct_version = EC_HOST_REQUEST_VERSION; |
69 | request->checksum = 0; |
70 | request->command = msg->command; |
71 | request->command_version = msg->version; |
72 | request->reserved = 0; |
73 | request->data_len = msg->outsize; |
74 | |
75 | for (i = 0; i < sizeof(*request); i++) |
76 | csum += out[i]; |
77 | |
78 | /* Copy data and update checksum */ |
79 | memcpy(out + sizeof(*request), msg->data, msg->outsize); |
80 | for (i = 0; i < msg->outsize; i++) |
81 | csum += msg->data[i]; |
82 | |
83 | request->checksum = -csum; |
84 | |
85 | return sizeof(*request) + msg->outsize; |
86 | } |
87 | |
88 | static int prepare_tx_legacy(struct cros_ec_device *ec_dev, |
89 | struct cros_ec_command *msg) |
90 | { |
91 | u8 *out; |
92 | u8 csum; |
93 | int i; |
94 | |
95 | if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE) |
96 | return -EINVAL; |
97 | |
98 | out = ec_dev->dout; |
99 | out[0] = EC_CMD_VERSION0 + msg->version; |
100 | out[1] = msg->command; |
101 | out[2] = msg->outsize; |
102 | csum = out[0] + out[1] + out[2]; |
103 | for (i = 0; i < msg->outsize; i++) |
104 | csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; |
105 | out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum; |
106 | |
107 | return EC_MSG_TX_PROTO_BYTES + msg->outsize; |
108 | } |
109 | |
110 | static int cros_ec_xfer_command(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) |
111 | { |
112 | int ret; |
113 | int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg); |
114 | |
115 | if (ec_dev->proto_version > 2) |
116 | xfer_fxn = ec_dev->pkt_xfer; |
117 | else |
118 | xfer_fxn = ec_dev->cmd_xfer; |
119 | |
120 | if (!xfer_fxn) { |
121 | /* |
122 | * This error can happen if a communication error happened and |
123 | * the EC is trying to use protocol v2, on an underlying |
124 | * communication mechanism that does not support v2. |
125 | */ |
126 | dev_err_once(ec_dev->dev, "missing EC transfer API, cannot send command\n" ); |
127 | return -EIO; |
128 | } |
129 | |
130 | trace_cros_ec_request_start(cmd: msg); |
131 | ret = (*xfer_fxn)(ec_dev, msg); |
132 | trace_cros_ec_request_done(cmd: msg, retval: ret); |
133 | |
134 | return ret; |
135 | } |
136 | |
137 | static int cros_ec_wait_until_complete(struct cros_ec_device *ec_dev, uint32_t *result) |
138 | { |
139 | struct { |
140 | struct cros_ec_command msg; |
141 | struct ec_response_get_comms_status status; |
142 | } __packed buf; |
143 | struct cros_ec_command *msg = &buf.msg; |
144 | struct ec_response_get_comms_status *status = &buf.status; |
145 | int ret = 0, i; |
146 | |
147 | msg->version = 0; |
148 | msg->command = EC_CMD_GET_COMMS_STATUS; |
149 | msg->insize = sizeof(*status); |
150 | msg->outsize = 0; |
151 | |
152 | /* Query the EC's status until it's no longer busy or we encounter an error. */ |
153 | for (i = 0; i < EC_COMMAND_RETRIES; ++i) { |
154 | usleep_range(min: 10000, max: 11000); |
155 | |
156 | ret = cros_ec_xfer_command(ec_dev, msg); |
157 | if (ret == -EAGAIN) |
158 | continue; |
159 | if (ret < 0) |
160 | return ret; |
161 | |
162 | *result = msg->result; |
163 | if (msg->result != EC_RES_SUCCESS) |
164 | return ret; |
165 | |
166 | if (ret == 0) { |
167 | ret = -EPROTO; |
168 | break; |
169 | } |
170 | |
171 | if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) |
172 | return ret; |
173 | } |
174 | |
175 | if (i >= EC_COMMAND_RETRIES) |
176 | ret = -EAGAIN; |
177 | |
178 | return ret; |
179 | } |
180 | |
181 | static int cros_ec_send_command(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) |
182 | { |
183 | int ret = cros_ec_xfer_command(ec_dev, msg); |
184 | |
185 | if (msg->result == EC_RES_IN_PROGRESS) |
186 | ret = cros_ec_wait_until_complete(ec_dev, result: &msg->result); |
187 | |
188 | return ret; |
189 | } |
190 | |
191 | /** |
192 | * cros_ec_prepare_tx() - Prepare an outgoing message in the output buffer. |
193 | * @ec_dev: Device to register. |
194 | * @msg: Message to write. |
195 | * |
196 | * This is used by all ChromeOS EC drivers to prepare the outgoing message |
197 | * according to different protocol versions. |
198 | * |
199 | * Return: number of prepared bytes on success or negative error code. |
200 | */ |
201 | int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, |
202 | struct cros_ec_command *msg) |
203 | { |
204 | if (ec_dev->proto_version > 2) |
205 | return prepare_tx(ec_dev, msg); |
206 | |
207 | return prepare_tx_legacy(ec_dev, msg); |
208 | } |
209 | EXPORT_SYMBOL(cros_ec_prepare_tx); |
210 | |
211 | /** |
212 | * cros_ec_check_result() - Check ec_msg->result. |
213 | * @ec_dev: EC device. |
214 | * @msg: Message to check. |
215 | * |
216 | * This is used by ChromeOS EC drivers to check the ec_msg->result for |
217 | * EC_RES_IN_PROGRESS and to warn about them. |
218 | * |
219 | * The function should not check for furthermore error codes. Otherwise, |
220 | * it would break the ABI. |
221 | * |
222 | * Return: -EAGAIN if ec_msg->result == EC_RES_IN_PROGRESS. Otherwise, 0. |
223 | */ |
224 | int cros_ec_check_result(struct cros_ec_device *ec_dev, |
225 | struct cros_ec_command *msg) |
226 | { |
227 | switch (msg->result) { |
228 | case EC_RES_SUCCESS: |
229 | return 0; |
230 | case EC_RES_IN_PROGRESS: |
231 | dev_dbg(ec_dev->dev, "command 0x%02x in progress\n" , |
232 | msg->command); |
233 | return -EAGAIN; |
234 | default: |
235 | dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n" , |
236 | msg->command, msg->result); |
237 | return 0; |
238 | } |
239 | } |
240 | EXPORT_SYMBOL(cros_ec_check_result); |
241 | |
242 | /* |
243 | * cros_ec_get_host_event_wake_mask |
244 | * |
245 | * Get the mask of host events that cause wake from suspend. |
246 | * |
247 | * @ec_dev: EC device to call |
248 | * @msg: message structure to use |
249 | * @mask: result when function returns 0. |
250 | * |
251 | * LOCKING: |
252 | * the caller has ec_dev->lock mutex, or the caller knows there is |
253 | * no other command in progress. |
254 | */ |
255 | static int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev, uint32_t *mask) |
256 | { |
257 | struct cros_ec_command *msg; |
258 | struct ec_response_host_event_mask *r; |
259 | int ret, mapped; |
260 | |
261 | msg = kzalloc(size: sizeof(*msg) + sizeof(*r), GFP_KERNEL); |
262 | if (!msg) |
263 | return -ENOMEM; |
264 | |
265 | msg->command = EC_CMD_HOST_EVENT_GET_WAKE_MASK; |
266 | msg->insize = sizeof(*r); |
267 | |
268 | ret = cros_ec_send_command(ec_dev, msg); |
269 | if (ret < 0) |
270 | goto exit; |
271 | |
272 | mapped = cros_ec_map_error(result: msg->result); |
273 | if (mapped) { |
274 | ret = mapped; |
275 | goto exit; |
276 | } |
277 | |
278 | if (ret == 0) { |
279 | ret = -EPROTO; |
280 | goto exit; |
281 | } |
282 | |
283 | r = (struct ec_response_host_event_mask *)msg->data; |
284 | *mask = r->mask; |
285 | ret = 0; |
286 | exit: |
287 | kfree(objp: msg); |
288 | return ret; |
289 | } |
290 | |
291 | static int cros_ec_get_proto_info(struct cros_ec_device *ec_dev, int devidx) |
292 | { |
293 | struct cros_ec_command *msg; |
294 | struct ec_response_get_protocol_info *info; |
295 | int ret, mapped; |
296 | |
297 | ec_dev->proto_version = 3; |
298 | if (devidx > 0) |
299 | ec_dev->max_passthru = 0; |
300 | |
301 | msg = kzalloc(size: sizeof(*msg) + sizeof(*info), GFP_KERNEL); |
302 | if (!msg) |
303 | return -ENOMEM; |
304 | |
305 | msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO; |
306 | msg->insize = sizeof(*info); |
307 | |
308 | ret = cros_ec_send_command(ec_dev, msg); |
309 | /* |
310 | * Send command once again when timeout occurred. |
311 | * Fingerprint MCU (FPMCU) is restarted during system boot which |
312 | * introduces small window in which FPMCU won't respond for any |
313 | * messages sent by kernel. There is no need to wait before next |
314 | * attempt because we waited at least EC_MSG_DEADLINE_MS. |
315 | */ |
316 | if (ret == -ETIMEDOUT) |
317 | ret = cros_ec_send_command(ec_dev, msg); |
318 | |
319 | if (ret < 0) { |
320 | dev_dbg(ec_dev->dev, |
321 | "failed to check for EC[%d] protocol version: %d\n" , |
322 | devidx, ret); |
323 | goto exit; |
324 | } |
325 | |
326 | mapped = cros_ec_map_error(result: msg->result); |
327 | if (mapped) { |
328 | ret = mapped; |
329 | goto exit; |
330 | } |
331 | |
332 | if (ret == 0) { |
333 | ret = -EPROTO; |
334 | goto exit; |
335 | } |
336 | |
337 | info = (struct ec_response_get_protocol_info *)msg->data; |
338 | |
339 | switch (devidx) { |
340 | case CROS_EC_DEV_EC_INDEX: |
341 | ec_dev->max_request = info->max_request_packet_size - |
342 | sizeof(struct ec_host_request); |
343 | ec_dev->max_response = info->max_response_packet_size - |
344 | sizeof(struct ec_host_response); |
345 | ec_dev->proto_version = min(EC_HOST_REQUEST_VERSION, |
346 | fls(info->protocol_versions) - 1); |
347 | ec_dev->din_size = info->max_response_packet_size + EC_MAX_RESPONSE_OVERHEAD; |
348 | ec_dev->dout_size = info->max_request_packet_size + EC_MAX_REQUEST_OVERHEAD; |
349 | |
350 | dev_dbg(ec_dev->dev, "using proto v%u\n" , ec_dev->proto_version); |
351 | break; |
352 | case CROS_EC_DEV_PD_INDEX: |
353 | ec_dev->max_passthru = info->max_request_packet_size - |
354 | sizeof(struct ec_host_request); |
355 | |
356 | dev_dbg(ec_dev->dev, "found PD chip\n" ); |
357 | break; |
358 | default: |
359 | dev_dbg(ec_dev->dev, "unknown passthru index: %d\n" , devidx); |
360 | break; |
361 | } |
362 | |
363 | ret = 0; |
364 | exit: |
365 | kfree(objp: msg); |
366 | return ret; |
367 | } |
368 | |
369 | static int cros_ec_get_proto_info_legacy(struct cros_ec_device *ec_dev) |
370 | { |
371 | struct cros_ec_command *msg; |
372 | struct ec_params_hello *params; |
373 | struct ec_response_hello *response; |
374 | int ret, mapped; |
375 | |
376 | ec_dev->proto_version = 2; |
377 | |
378 | msg = kzalloc(size: sizeof(*msg) + max(sizeof(*params), sizeof(*response)), GFP_KERNEL); |
379 | if (!msg) |
380 | return -ENOMEM; |
381 | |
382 | msg->command = EC_CMD_HELLO; |
383 | msg->insize = sizeof(*response); |
384 | msg->outsize = sizeof(*params); |
385 | |
386 | params = (struct ec_params_hello *)msg->data; |
387 | params->in_data = 0xa0b0c0d0; |
388 | |
389 | ret = cros_ec_send_command(ec_dev, msg); |
390 | if (ret < 0) { |
391 | dev_dbg(ec_dev->dev, "EC failed to respond to v2 hello: %d\n" , ret); |
392 | goto exit; |
393 | } |
394 | |
395 | mapped = cros_ec_map_error(result: msg->result); |
396 | if (mapped) { |
397 | ret = mapped; |
398 | dev_err(ec_dev->dev, "EC responded to v2 hello with error: %d\n" , msg->result); |
399 | goto exit; |
400 | } |
401 | |
402 | if (ret == 0) { |
403 | ret = -EPROTO; |
404 | goto exit; |
405 | } |
406 | |
407 | response = (struct ec_response_hello *)msg->data; |
408 | if (response->out_data != 0xa1b2c3d4) { |
409 | dev_err(ec_dev->dev, |
410 | "EC responded to v2 hello with bad result: %u\n" , |
411 | response->out_data); |
412 | ret = -EBADMSG; |
413 | goto exit; |
414 | } |
415 | |
416 | ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE; |
417 | ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE; |
418 | ec_dev->max_passthru = 0; |
419 | ec_dev->pkt_xfer = NULL; |
420 | ec_dev->din_size = EC_PROTO2_MSG_BYTES; |
421 | ec_dev->dout_size = EC_PROTO2_MSG_BYTES; |
422 | |
423 | dev_dbg(ec_dev->dev, "falling back to proto v2\n" ); |
424 | ret = 0; |
425 | exit: |
426 | kfree(objp: msg); |
427 | return ret; |
428 | } |
429 | |
430 | /* |
431 | * cros_ec_get_host_command_version_mask |
432 | * |
433 | * Get the version mask of a given command. |
434 | * |
435 | * @ec_dev: EC device to call |
436 | * @msg: message structure to use |
437 | * @cmd: command to get the version of. |
438 | * @mask: result when function returns 0. |
439 | * |
440 | * @return 0 on success, error code otherwise |
441 | * |
442 | * LOCKING: |
443 | * the caller has ec_dev->lock mutex or the caller knows there is |
444 | * no other command in progress. |
445 | */ |
446 | static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, u16 cmd, u32 *mask) |
447 | { |
448 | struct ec_params_get_cmd_versions *pver; |
449 | struct ec_response_get_cmd_versions *rver; |
450 | struct cros_ec_command *msg; |
451 | int ret, mapped; |
452 | |
453 | msg = kmalloc(size: sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)), |
454 | GFP_KERNEL); |
455 | if (!msg) |
456 | return -ENOMEM; |
457 | |
458 | msg->version = 0; |
459 | msg->command = EC_CMD_GET_CMD_VERSIONS; |
460 | msg->insize = sizeof(*rver); |
461 | msg->outsize = sizeof(*pver); |
462 | |
463 | pver = (struct ec_params_get_cmd_versions *)msg->data; |
464 | pver->cmd = cmd; |
465 | |
466 | ret = cros_ec_send_command(ec_dev, msg); |
467 | if (ret < 0) |
468 | goto exit; |
469 | |
470 | mapped = cros_ec_map_error(result: msg->result); |
471 | if (mapped) { |
472 | ret = mapped; |
473 | goto exit; |
474 | } |
475 | |
476 | if (ret == 0) { |
477 | ret = -EPROTO; |
478 | goto exit; |
479 | } |
480 | |
481 | rver = (struct ec_response_get_cmd_versions *)msg->data; |
482 | *mask = rver->version_mask; |
483 | ret = 0; |
484 | exit: |
485 | kfree(objp: msg); |
486 | return ret; |
487 | } |
488 | |
489 | /** |
490 | * cros_ec_query_all() - Query the protocol version supported by the |
491 | * ChromeOS EC. |
492 | * @ec_dev: Device to register. |
493 | * |
494 | * Return: 0 on success or negative error code. |
495 | */ |
496 | int cros_ec_query_all(struct cros_ec_device *ec_dev) |
497 | { |
498 | struct device *dev = ec_dev->dev; |
499 | u32 ver_mask; |
500 | int ret; |
501 | |
502 | /* First try sending with proto v3. */ |
503 | if (!cros_ec_get_proto_info(ec_dev, CROS_EC_DEV_EC_INDEX)) { |
504 | /* Check for PD. */ |
505 | cros_ec_get_proto_info(ec_dev, CROS_EC_DEV_PD_INDEX); |
506 | } else { |
507 | /* Try querying with a v2 hello message. */ |
508 | ret = cros_ec_get_proto_info_legacy(ec_dev); |
509 | if (ret) { |
510 | /* |
511 | * It's possible for a test to occur too early when |
512 | * the EC isn't listening. If this happens, we'll |
513 | * test later when the first command is run. |
514 | */ |
515 | ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN; |
516 | dev_dbg(ec_dev->dev, "EC query failed: %d\n" , ret); |
517 | return ret; |
518 | } |
519 | } |
520 | |
521 | devm_kfree(dev, p: ec_dev->din); |
522 | devm_kfree(dev, p: ec_dev->dout); |
523 | |
524 | ec_dev->din = devm_kzalloc(dev, size: ec_dev->din_size, GFP_KERNEL); |
525 | if (!ec_dev->din) { |
526 | ret = -ENOMEM; |
527 | goto exit; |
528 | } |
529 | |
530 | ec_dev->dout = devm_kzalloc(dev, size: ec_dev->dout_size, GFP_KERNEL); |
531 | if (!ec_dev->dout) { |
532 | devm_kfree(dev, p: ec_dev->din); |
533 | ret = -ENOMEM; |
534 | goto exit; |
535 | } |
536 | |
537 | /* Probe if MKBP event is supported */ |
538 | ret = cros_ec_get_host_command_version_mask(ec_dev, EC_CMD_GET_NEXT_EVENT, mask: &ver_mask); |
539 | if (ret < 0 || ver_mask == 0) { |
540 | ec_dev->mkbp_event_supported = 0; |
541 | } else { |
542 | ec_dev->mkbp_event_supported = fls(x: ver_mask); |
543 | |
544 | dev_dbg(ec_dev->dev, "MKBP support version %u\n" , ec_dev->mkbp_event_supported - 1); |
545 | } |
546 | |
547 | /* Probe if host sleep v1 is supported for S0ix failure detection. */ |
548 | ret = cros_ec_get_host_command_version_mask(ec_dev, EC_CMD_HOST_SLEEP_EVENT, mask: &ver_mask); |
549 | ec_dev->host_sleep_v1 = (ret == 0 && (ver_mask & EC_VER_MASK(1))); |
550 | |
551 | /* Get host event wake mask. */ |
552 | ret = cros_ec_get_host_event_wake_mask(ec_dev, mask: &ec_dev->host_event_wake_mask); |
553 | if (ret < 0) { |
554 | /* |
555 | * If the EC doesn't support EC_CMD_HOST_EVENT_GET_WAKE_MASK, |
556 | * use a reasonable default. Note that we ignore various |
557 | * battery, AC status, and power-state events, because (a) |
558 | * those can be quite common (e.g., when sitting at full |
559 | * charge, on AC) and (b) these are not actionable wake events; |
560 | * if anything, we'd like to continue suspending (to save |
561 | * power), not wake up. |
562 | */ |
563 | ec_dev->host_event_wake_mask = U32_MAX & |
564 | ~(EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED) | |
565 | EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED) | |
566 | EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_LOW) | |
567 | EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_CRITICAL) | |
568 | EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY) | |
569 | EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) | |
570 | EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_STATUS)); |
571 | /* |
572 | * Old ECs may not support this command. Complain about all |
573 | * other errors. |
574 | */ |
575 | if (ret != -EOPNOTSUPP) |
576 | dev_err(ec_dev->dev, |
577 | "failed to retrieve wake mask: %d\n" , ret); |
578 | } |
579 | |
580 | ret = 0; |
581 | |
582 | exit: |
583 | return ret; |
584 | } |
585 | EXPORT_SYMBOL(cros_ec_query_all); |
586 | |
587 | /** |
588 | * cros_ec_cmd_xfer() - Send a command to the ChromeOS EC. |
589 | * @ec_dev: EC device. |
590 | * @msg: Message to write. |
591 | * |
592 | * Call this to send a command to the ChromeOS EC. This should be used instead |
593 | * of calling the EC's cmd_xfer() callback directly. This function does not |
594 | * convert EC command execution error codes to Linux error codes. Most |
595 | * in-kernel users will want to use cros_ec_cmd_xfer_status() instead since |
596 | * that function implements the conversion. |
597 | * |
598 | * Return: |
599 | * >0 - EC command was executed successfully. The return value is the number |
600 | * of bytes returned by the EC (excluding the header). |
601 | * =0 - EC communication was successful. EC command execution results are |
602 | * reported in msg->result. The result will be EC_RES_SUCCESS if the |
603 | * command was executed successfully or report an EC command execution |
604 | * error. |
605 | * <0 - EC communication error. Return value is the Linux error code. |
606 | */ |
607 | int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) |
608 | { |
609 | int ret; |
610 | |
611 | mutex_lock(&ec_dev->lock); |
612 | if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) { |
613 | ret = cros_ec_query_all(ec_dev); |
614 | if (ret) { |
615 | dev_err(ec_dev->dev, |
616 | "EC version unknown and query failed; aborting command\n" ); |
617 | mutex_unlock(lock: &ec_dev->lock); |
618 | return ret; |
619 | } |
620 | } |
621 | |
622 | if (msg->insize > ec_dev->max_response) { |
623 | dev_dbg(ec_dev->dev, "clamping message receive buffer\n" ); |
624 | msg->insize = ec_dev->max_response; |
625 | } |
626 | |
627 | if (msg->command < EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX)) { |
628 | if (msg->outsize > ec_dev->max_request) { |
629 | dev_err(ec_dev->dev, |
630 | "request of size %u is too big (max: %u)\n" , |
631 | msg->outsize, |
632 | ec_dev->max_request); |
633 | mutex_unlock(lock: &ec_dev->lock); |
634 | return -EMSGSIZE; |
635 | } |
636 | } else { |
637 | if (msg->outsize > ec_dev->max_passthru) { |
638 | dev_err(ec_dev->dev, |
639 | "passthru rq of size %u is too big (max: %u)\n" , |
640 | msg->outsize, |
641 | ec_dev->max_passthru); |
642 | mutex_unlock(lock: &ec_dev->lock); |
643 | return -EMSGSIZE; |
644 | } |
645 | } |
646 | |
647 | ret = cros_ec_send_command(ec_dev, msg); |
648 | mutex_unlock(lock: &ec_dev->lock); |
649 | |
650 | return ret; |
651 | } |
652 | EXPORT_SYMBOL(cros_ec_cmd_xfer); |
653 | |
654 | /** |
655 | * cros_ec_cmd_xfer_status() - Send a command to the ChromeOS EC. |
656 | * @ec_dev: EC device. |
657 | * @msg: Message to write. |
658 | * |
659 | * Call this to send a command to the ChromeOS EC. This should be used instead of calling the EC's |
660 | * cmd_xfer() callback directly. It returns success status only if both the command was transmitted |
661 | * successfully and the EC replied with success status. |
662 | * |
663 | * Return: |
664 | * >=0 - The number of bytes transferred. |
665 | * <0 - Linux error code |
666 | */ |
667 | int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, |
668 | struct cros_ec_command *msg) |
669 | { |
670 | int ret, mapped; |
671 | |
672 | ret = cros_ec_cmd_xfer(ec_dev, msg); |
673 | if (ret < 0) |
674 | return ret; |
675 | |
676 | mapped = cros_ec_map_error(result: msg->result); |
677 | if (mapped) { |
678 | dev_dbg(ec_dev->dev, "Command result (err: %d [%d])\n" , |
679 | msg->result, mapped); |
680 | ret = mapped; |
681 | } |
682 | |
683 | return ret; |
684 | } |
685 | EXPORT_SYMBOL(cros_ec_cmd_xfer_status); |
686 | |
687 | static int get_next_event_xfer(struct cros_ec_device *ec_dev, |
688 | struct cros_ec_command *msg, |
689 | struct ec_response_get_next_event_v1 *event, |
690 | int version, uint32_t size) |
691 | { |
692 | int ret; |
693 | |
694 | msg->version = version; |
695 | msg->command = EC_CMD_GET_NEXT_EVENT; |
696 | msg->insize = size; |
697 | msg->outsize = 0; |
698 | |
699 | ret = cros_ec_cmd_xfer_status(ec_dev, msg); |
700 | if (ret > 0) { |
701 | ec_dev->event_size = ret - 1; |
702 | ec_dev->event_data = *event; |
703 | } |
704 | |
705 | return ret; |
706 | } |
707 | |
708 | static int get_next_event(struct cros_ec_device *ec_dev) |
709 | { |
710 | struct { |
711 | struct cros_ec_command msg; |
712 | struct ec_response_get_next_event_v1 event; |
713 | } __packed buf; |
714 | struct cros_ec_command *msg = &buf.msg; |
715 | struct ec_response_get_next_event_v1 *event = &buf.event; |
716 | const int cmd_version = ec_dev->mkbp_event_supported - 1; |
717 | |
718 | memset(msg, 0, sizeof(*msg)); |
719 | if (ec_dev->suspended) { |
720 | dev_dbg(ec_dev->dev, "Device suspended.\n" ); |
721 | return -EHOSTDOWN; |
722 | } |
723 | |
724 | if (cmd_version == 0) |
725 | return get_next_event_xfer(ec_dev, msg, event, version: 0, |
726 | size: sizeof(struct ec_response_get_next_event)); |
727 | |
728 | return get_next_event_xfer(ec_dev, msg, event, version: cmd_version, |
729 | size: sizeof(struct ec_response_get_next_event_v1)); |
730 | } |
731 | |
732 | static int get_keyboard_state_event(struct cros_ec_device *ec_dev) |
733 | { |
734 | u8 buffer[sizeof(struct cros_ec_command) + |
735 | sizeof(ec_dev->event_data.data)]; |
736 | struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; |
737 | |
738 | msg->version = 0; |
739 | msg->command = EC_CMD_MKBP_STATE; |
740 | msg->insize = sizeof(ec_dev->event_data.data); |
741 | msg->outsize = 0; |
742 | |
743 | ec_dev->event_size = cros_ec_cmd_xfer_status(ec_dev, msg); |
744 | ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX; |
745 | memcpy(&ec_dev->event_data.data, msg->data, |
746 | sizeof(ec_dev->event_data.data)); |
747 | |
748 | return ec_dev->event_size; |
749 | } |
750 | |
751 | /** |
752 | * cros_ec_get_next_event() - Fetch next event from the ChromeOS EC. |
753 | * @ec_dev: Device to fetch event from. |
754 | * @wake_event: Pointer to a bool set to true upon return if the event might be |
755 | * treated as a wake event. Ignored if null. |
756 | * @has_more_events: Pointer to bool set to true if more than one event is |
757 | * pending. |
758 | * Some EC will set this flag to indicate cros_ec_get_next_event() |
759 | * can be called multiple times in a row. |
760 | * It is an optimization to prevent issuing a EC command for |
761 | * nothing or wait for another interrupt from the EC to process |
762 | * the next message. |
763 | * Ignored if null. |
764 | * |
765 | * Return: negative error code on errors; 0 for no data; or else number of |
766 | * bytes received (i.e., an event was retrieved successfully). Event types are |
767 | * written out to @ec_dev->event_data.event_type on success. |
768 | */ |
769 | int cros_ec_get_next_event(struct cros_ec_device *ec_dev, |
770 | bool *wake_event, |
771 | bool *has_more_events) |
772 | { |
773 | u8 event_type; |
774 | u32 host_event; |
775 | int ret; |
776 | u32 ver_mask; |
777 | |
778 | /* |
779 | * Default value for wake_event. |
780 | * Wake up on keyboard event, wake up for spurious interrupt or link |
781 | * error to the EC. |
782 | */ |
783 | if (wake_event) |
784 | *wake_event = true; |
785 | |
786 | /* |
787 | * Default value for has_more_events. |
788 | * EC will raise another interrupt if AP does not process all events |
789 | * anyway. |
790 | */ |
791 | if (has_more_events) |
792 | *has_more_events = false; |
793 | |
794 | if (!ec_dev->mkbp_event_supported) |
795 | return get_keyboard_state_event(ec_dev); |
796 | |
797 | ret = get_next_event(ec_dev); |
798 | /* |
799 | * -ENOPROTOOPT is returned when EC returns EC_RES_INVALID_VERSION. |
800 | * This can occur when EC based device (e.g. Fingerprint MCU) jumps to |
801 | * the RO image which doesn't support newer version of the command. In |
802 | * this case we will attempt to update maximum supported version of the |
803 | * EC_CMD_GET_NEXT_EVENT. |
804 | */ |
805 | if (ret == -ENOPROTOOPT) { |
806 | dev_dbg(ec_dev->dev, |
807 | "GET_NEXT_EVENT returned invalid version error.\n" ); |
808 | ret = cros_ec_get_host_command_version_mask(ec_dev, |
809 | EC_CMD_GET_NEXT_EVENT, |
810 | mask: &ver_mask); |
811 | if (ret < 0 || ver_mask == 0) |
812 | /* |
813 | * Do not change the MKBP supported version if we can't |
814 | * obtain supported version correctly. Please note that |
815 | * calling EC_CMD_GET_NEXT_EVENT returned |
816 | * EC_RES_INVALID_VERSION which means that the command |
817 | * is present. |
818 | */ |
819 | return -ENOPROTOOPT; |
820 | |
821 | ec_dev->mkbp_event_supported = fls(x: ver_mask); |
822 | dev_dbg(ec_dev->dev, "MKBP support version changed to %u\n" , |
823 | ec_dev->mkbp_event_supported - 1); |
824 | |
825 | /* Try to get next event with new MKBP support version set. */ |
826 | ret = get_next_event(ec_dev); |
827 | } |
828 | |
829 | if (ret <= 0) |
830 | return ret; |
831 | |
832 | if (has_more_events) |
833 | *has_more_events = ec_dev->event_data.event_type & |
834 | EC_MKBP_HAS_MORE_EVENTS; |
835 | ec_dev->event_data.event_type &= EC_MKBP_EVENT_TYPE_MASK; |
836 | |
837 | if (wake_event) { |
838 | event_type = ec_dev->event_data.event_type; |
839 | host_event = cros_ec_get_host_event(ec_dev); |
840 | |
841 | /* |
842 | * Sensor events need to be parsed by the sensor sub-device. |
843 | * Defer them, and don't report the wakeup here. |
844 | */ |
845 | if (event_type == EC_MKBP_EVENT_SENSOR_FIFO) { |
846 | *wake_event = false; |
847 | } else if (host_event) { |
848 | /* rtc_update_irq() already handles wakeup events. */ |
849 | if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) |
850 | *wake_event = false; |
851 | /* Masked host-events should not count as wake events. */ |
852 | if (!(host_event & ec_dev->host_event_wake_mask)) |
853 | *wake_event = false; |
854 | } |
855 | } |
856 | |
857 | return ret; |
858 | } |
859 | EXPORT_SYMBOL(cros_ec_get_next_event); |
860 | |
861 | /** |
862 | * cros_ec_get_host_event() - Return a mask of event set by the ChromeOS EC. |
863 | * @ec_dev: Device to fetch event from. |
864 | * |
865 | * When MKBP is supported, when the EC raises an interrupt, we collect the |
866 | * events raised and call the functions in the ec notifier. This function |
867 | * is a helper to know which events are raised. |
868 | * |
869 | * Return: 0 on error or non-zero bitmask of one or more EC_HOST_EVENT_*. |
870 | */ |
871 | u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev) |
872 | { |
873 | u32 host_event; |
874 | |
875 | if (!ec_dev->mkbp_event_supported) |
876 | return 0; |
877 | |
878 | if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT) |
879 | return 0; |
880 | |
881 | if (ec_dev->event_size != sizeof(host_event)) { |
882 | dev_warn(ec_dev->dev, "Invalid host event size\n" ); |
883 | return 0; |
884 | } |
885 | |
886 | host_event = get_unaligned_le32(p: &ec_dev->event_data.data.host_event); |
887 | |
888 | return host_event; |
889 | } |
890 | EXPORT_SYMBOL(cros_ec_get_host_event); |
891 | |
892 | /** |
893 | * cros_ec_check_features() - Test for the presence of EC features |
894 | * |
895 | * @ec: EC device, does not have to be connected directly to the AP, |
896 | * can be daisy chained through another device. |
897 | * @feature: One of ec_feature_code bit. |
898 | * |
899 | * Call this function to test whether the ChromeOS EC supports a feature. |
900 | * |
901 | * Return: true if supported, false if not (or if an error was encountered). |
902 | */ |
903 | bool cros_ec_check_features(struct cros_ec_dev *ec, int feature) |
904 | { |
905 | struct ec_response_get_features *features = &ec->features; |
906 | int ret; |
907 | |
908 | if (features->flags[0] == -1U && features->flags[1] == -1U) { |
909 | /* features bitmap not read yet */ |
910 | ret = cros_ec_cmd(ec_dev: ec->ec_dev, version: 0, EC_CMD_GET_FEATURES + ec->cmd_offset, |
911 | NULL, outsize: 0, indata: features, insize: sizeof(*features)); |
912 | if (ret < 0) { |
913 | dev_warn(ec->dev, "cannot get EC features: %d\n" , ret); |
914 | memset(features, 0, sizeof(*features)); |
915 | } |
916 | |
917 | dev_dbg(ec->dev, "EC features %08x %08x\n" , |
918 | features->flags[0], features->flags[1]); |
919 | } |
920 | |
921 | return !!(features->flags[feature / 32] & EC_FEATURE_MASK_0(feature)); |
922 | } |
923 | EXPORT_SYMBOL_GPL(cros_ec_check_features); |
924 | |
925 | /** |
926 | * cros_ec_get_sensor_count() - Return the number of MEMS sensors supported. |
927 | * |
928 | * @ec: EC device, does not have to be connected directly to the AP, |
929 | * can be daisy chained through another device. |
930 | * Return: < 0 in case of error. |
931 | */ |
932 | int cros_ec_get_sensor_count(struct cros_ec_dev *ec) |
933 | { |
934 | /* |
935 | * Issue a command to get the number of sensor reported. |
936 | * If not supported, check for legacy mode. |
937 | */ |
938 | int ret, sensor_count; |
939 | struct ec_params_motion_sense *params; |
940 | struct ec_response_motion_sense *resp; |
941 | struct cros_ec_command *msg; |
942 | struct cros_ec_device *ec_dev = ec->ec_dev; |
943 | u8 status; |
944 | |
945 | msg = kzalloc(size: sizeof(*msg) + max(sizeof(*params), sizeof(*resp)), |
946 | GFP_KERNEL); |
947 | if (!msg) |
948 | return -ENOMEM; |
949 | |
950 | msg->version = 1; |
951 | msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; |
952 | msg->outsize = sizeof(*params); |
953 | msg->insize = sizeof(*resp); |
954 | |
955 | params = (struct ec_params_motion_sense *)msg->data; |
956 | params->cmd = MOTIONSENSE_CMD_DUMP; |
957 | |
958 | ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); |
959 | if (ret < 0) { |
960 | sensor_count = ret; |
961 | } else { |
962 | resp = (struct ec_response_motion_sense *)msg->data; |
963 | sensor_count = resp->dump.sensor_count; |
964 | } |
965 | kfree(objp: msg); |
966 | |
967 | /* |
968 | * Check legacy mode: Let's find out if sensors are accessible |
969 | * via LPC interface. |
970 | */ |
971 | if (sensor_count < 0 && ec->cmd_offset == 0 && ec_dev->cmd_readmem) { |
972 | ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, |
973 | 1, &status); |
974 | if (ret >= 0 && |
975 | (status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) { |
976 | /* |
977 | * We have 2 sensors, one in the lid, one in the base. |
978 | */ |
979 | sensor_count = 2; |
980 | } else { |
981 | /* |
982 | * EC uses LPC interface and no sensors are presented. |
983 | */ |
984 | sensor_count = 0; |
985 | } |
986 | } |
987 | return sensor_count; |
988 | } |
989 | EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count); |
990 | |
991 | /** |
992 | * cros_ec_cmd - Send a command to the EC. |
993 | * |
994 | * @ec_dev: EC device |
995 | * @version: EC command version |
996 | * @command: EC command |
997 | * @outdata: EC command output data |
998 | * @outsize: Size of outdata |
999 | * @indata: EC command input data |
1000 | * @insize: Size of indata |
1001 | * |
1002 | * Return: >= 0 on success, negative error number on failure. |
1003 | */ |
1004 | int cros_ec_cmd(struct cros_ec_device *ec_dev, |
1005 | unsigned int version, |
1006 | int command, |
1007 | const void *outdata, |
1008 | size_t outsize, |
1009 | void *indata, |
1010 | size_t insize) |
1011 | { |
1012 | struct cros_ec_command *msg; |
1013 | int ret; |
1014 | |
1015 | msg = kzalloc(size: sizeof(*msg) + max(insize, outsize), GFP_KERNEL); |
1016 | if (!msg) |
1017 | return -ENOMEM; |
1018 | |
1019 | msg->version = version; |
1020 | msg->command = command; |
1021 | msg->outsize = outsize; |
1022 | msg->insize = insize; |
1023 | |
1024 | if (outsize) |
1025 | memcpy(msg->data, outdata, outsize); |
1026 | |
1027 | ret = cros_ec_cmd_xfer_status(ec_dev, msg); |
1028 | if (ret < 0) |
1029 | goto error; |
1030 | |
1031 | if (insize) |
1032 | memcpy(indata, msg->data, insize); |
1033 | error: |
1034 | kfree(objp: msg); |
1035 | return ret; |
1036 | } |
1037 | EXPORT_SYMBOL_GPL(cros_ec_cmd); |
1038 | |