]>
Commit | Line | Data |
---|---|---|
062476f2 JMC |
1 | /* |
2 | * ChromeOS EC communication protocol helper functions | |
3 | * | |
4 | * Copyright (C) 2015 Google, Inc | |
5 | * | |
6 | * This software is licensed under the terms of the GNU General Public | |
7 | * License version 2, as published by the Free Software Foundation, and | |
8 | * may be copied, distributed, and modified under those terms. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <linux/mfd/cros_ec.h> | |
18 | #include <linux/delay.h> | |
19 | #include <linux/device.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/slab.h> | |
6f1d912b | 22 | #include <asm/unaligned.h> |
062476f2 JMC |
23 | |
24 | #define EC_COMMAND_RETRIES 50 | |
25 | ||
2c7589af SB |
26 | static int prepare_packet(struct cros_ec_device *ec_dev, |
27 | struct cros_ec_command *msg) | |
28 | { | |
29 | struct ec_host_request *request; | |
30 | u8 *out; | |
31 | int i; | |
32 | u8 csum = 0; | |
33 | ||
34 | BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION); | |
35 | BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size); | |
36 | ||
37 | out = ec_dev->dout; | |
38 | request = (struct ec_host_request *)out; | |
39 | request->struct_version = EC_HOST_REQUEST_VERSION; | |
40 | request->checksum = 0; | |
41 | request->command = msg->command; | |
42 | request->command_version = msg->version; | |
43 | request->reserved = 0; | |
44 | request->data_len = msg->outsize; | |
45 | ||
46 | for (i = 0; i < sizeof(*request); i++) | |
47 | csum += out[i]; | |
48 | ||
49 | /* Copy data and update checksum */ | |
50 | memcpy(out + sizeof(*request), msg->data, msg->outsize); | |
51 | for (i = 0; i < msg->outsize; i++) | |
52 | csum += msg->data[i]; | |
53 | ||
54 | request->checksum = -csum; | |
55 | ||
56 | return sizeof(*request) + msg->outsize; | |
57 | } | |
58 | ||
59 | static int send_command(struct cros_ec_device *ec_dev, | |
60 | struct cros_ec_command *msg) | |
61 | { | |
62 | int ret; | |
63 | ||
64 | if (ec_dev->proto_version > 2) | |
65 | ret = ec_dev->pkt_xfer(ec_dev, msg); | |
66 | else | |
67 | ret = ec_dev->cmd_xfer(ec_dev, msg); | |
68 | ||
69 | if (msg->result == EC_RES_IN_PROGRESS) { | |
70 | int i; | |
71 | struct cros_ec_command *status_msg; | |
72 | struct ec_response_get_comms_status *status; | |
73 | ||
74 | status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), | |
75 | GFP_KERNEL); | |
76 | if (!status_msg) | |
77 | return -ENOMEM; | |
78 | ||
79 | status_msg->version = 0; | |
80 | status_msg->command = EC_CMD_GET_COMMS_STATUS; | |
81 | status_msg->insize = sizeof(*status); | |
82 | status_msg->outsize = 0; | |
83 | ||
84 | /* | |
85 | * Query the EC's status until it's no longer busy or | |
86 | * we encounter an error. | |
87 | */ | |
88 | for (i = 0; i < EC_COMMAND_RETRIES; i++) { | |
89 | usleep_range(10000, 11000); | |
90 | ||
91 | ret = ec_dev->cmd_xfer(ec_dev, status_msg); | |
92 | if (ret < 0) | |
93 | break; | |
94 | ||
95 | msg->result = status_msg->result; | |
96 | if (status_msg->result != EC_RES_SUCCESS) | |
97 | break; | |
98 | ||
99 | status = (struct ec_response_get_comms_status *) | |
100 | status_msg->data; | |
101 | if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) | |
102 | break; | |
103 | } | |
104 | ||
105 | kfree(status_msg); | |
106 | } | |
107 | ||
108 | return ret; | |
109 | } | |
110 | ||
062476f2 JMC |
111 | int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, |
112 | struct cros_ec_command *msg) | |
113 | { | |
2c7589af SB |
114 | u8 *out; |
115 | u8 csum; | |
116 | int i; | |
117 | ||
118 | if (ec_dev->proto_version > 2) | |
119 | return prepare_packet(ec_dev, msg); | |
062476f2 JMC |
120 | |
121 | BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); | |
122 | out = ec_dev->dout; | |
123 | out[0] = EC_CMD_VERSION0 + msg->version; | |
124 | out[1] = msg->command; | |
125 | out[2] = msg->outsize; | |
126 | csum = out[0] + out[1] + out[2]; | |
127 | for (i = 0; i < msg->outsize; i++) | |
128 | csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; | |
2c7589af | 129 | out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum; |
062476f2 JMC |
130 | |
131 | return EC_MSG_TX_PROTO_BYTES + msg->outsize; | |
132 | } | |
133 | EXPORT_SYMBOL(cros_ec_prepare_tx); | |
134 | ||
135 | int cros_ec_check_result(struct cros_ec_device *ec_dev, | |
136 | struct cros_ec_command *msg) | |
137 | { | |
138 | switch (msg->result) { | |
139 | case EC_RES_SUCCESS: | |
140 | return 0; | |
141 | case EC_RES_IN_PROGRESS: | |
142 | dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", | |
143 | msg->command); | |
144 | return -EAGAIN; | |
145 | default: | |
146 | dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", | |
147 | msg->command, msg->result); | |
148 | return 0; | |
149 | } | |
150 | } | |
151 | EXPORT_SYMBOL(cros_ec_check_result); | |
152 | ||
29d99b96 SN |
153 | /* |
154 | * cros_ec_get_host_event_wake_mask | |
155 | * | |
156 | * Get the mask of host events that cause wake from suspend. | |
157 | * | |
158 | * @ec_dev: EC device to call | |
159 | * @msg: message structure to use | |
160 | * @mask: result when function returns >=0. | |
161 | * | |
162 | * LOCKING: | |
163 | * the caller has ec_dev->lock mutex, or the caller knows there is | |
164 | * no other command in progress. | |
165 | */ | |
166 | static int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev, | |
167 | struct cros_ec_command *msg, | |
168 | uint32_t *mask) | |
169 | { | |
170 | struct ec_response_host_event_mask *r; | |
171 | int ret; | |
172 | ||
173 | msg->command = EC_CMD_HOST_EVENT_GET_WAKE_MASK; | |
174 | msg->version = 0; | |
175 | msg->outsize = 0; | |
176 | msg->insize = sizeof(*r); | |
177 | ||
178 | ret = send_command(ec_dev, msg); | |
179 | if (ret > 0) { | |
180 | r = (struct ec_response_host_event_mask *)msg->data; | |
181 | *mask = r->mask; | |
182 | } | |
183 | ||
184 | return ret; | |
185 | } | |
186 | ||
2c7589af SB |
187 | static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev, |
188 | int devidx, | |
189 | struct cros_ec_command *msg) | |
062476f2 | 190 | { |
2c7589af SB |
191 | /* |
192 | * Try using v3+ to query for supported protocols. If this | |
193 | * command fails, fall back to v2. Returns the highest protocol | |
194 | * supported by the EC. | |
195 | * Also sets the max request/response/passthru size. | |
196 | */ | |
062476f2 JMC |
197 | int ret; |
198 | ||
2c7589af SB |
199 | if (!ec_dev->pkt_xfer) |
200 | return -EPROTONOSUPPORT; | |
062476f2 | 201 | |
2c7589af SB |
202 | memset(msg, 0, sizeof(*msg)); |
203 | msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO; | |
204 | msg->insize = sizeof(struct ec_response_get_protocol_info); | |
062476f2 | 205 | |
2c7589af SB |
206 | ret = send_command(ec_dev, msg); |
207 | ||
208 | if (ret < 0) { | |
209 | dev_dbg(ec_dev->dev, | |
210 | "failed to check for EC[%d] protocol version: %d\n", | |
211 | devidx, ret); | |
212 | return ret; | |
213 | } | |
214 | ||
215 | if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND) | |
216 | return -ENODEV; | |
217 | else if (msg->result != EC_RES_SUCCESS) | |
218 | return msg->result; | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev) | |
224 | { | |
225 | struct cros_ec_command *msg; | |
226 | struct ec_params_hello *hello_params; | |
227 | struct ec_response_hello *hello_response; | |
228 | int ret; | |
229 | int len = max(sizeof(*hello_params), sizeof(*hello_response)); | |
230 | ||
231 | msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL); | |
232 | if (!msg) | |
233 | return -ENOMEM; | |
234 | ||
235 | msg->version = 0; | |
236 | msg->command = EC_CMD_HELLO; | |
237 | hello_params = (struct ec_params_hello *)msg->data; | |
238 | msg->outsize = sizeof(*hello_params); | |
239 | hello_response = (struct ec_response_hello *)msg->data; | |
240 | msg->insize = sizeof(*hello_response); | |
241 | ||
242 | hello_params->in_data = 0xa0b0c0d0; | |
243 | ||
244 | ret = send_command(ec_dev, msg); | |
245 | ||
246 | if (ret < 0) { | |
247 | dev_dbg(ec_dev->dev, | |
248 | "EC failed to respond to v2 hello: %d\n", | |
249 | ret); | |
250 | goto exit; | |
251 | } else if (msg->result != EC_RES_SUCCESS) { | |
252 | dev_err(ec_dev->dev, | |
253 | "EC responded to v2 hello with error: %d\n", | |
254 | msg->result); | |
255 | ret = msg->result; | |
256 | goto exit; | |
257 | } else if (hello_response->out_data != 0xa1b2c3d4) { | |
258 | dev_err(ec_dev->dev, | |
259 | "EC responded to v2 hello with bad result: %u\n", | |
260 | hello_response->out_data); | |
261 | ret = -EBADMSG; | |
262 | goto exit; | |
263 | } | |
264 | ||
265 | ret = 0; | |
266 | ||
267 | exit: | |
268 | kfree(msg); | |
269 | return ret; | |
270 | } | |
271 | ||
d4da97e5 GG |
272 | /* |
273 | * cros_ec_get_host_command_version_mask | |
274 | * | |
275 | * Get the version mask of a given command. | |
276 | * | |
277 | * @ec_dev: EC device to call | |
278 | * @msg: message structure to use | |
279 | * @cmd: command to get the version of. | |
280 | * @mask: result when function returns 0. | |
281 | * | |
282 | * @return 0 on success, error code otherwise | |
283 | * | |
284 | * LOCKING: | |
285 | * the caller has ec_dev->lock mutex or the caller knows there is | |
286 | * no other command in progress. | |
287 | */ | |
6f1d912b VY |
288 | static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, |
289 | u16 cmd, u32 *mask) | |
290 | { | |
291 | struct ec_params_get_cmd_versions *pver; | |
292 | struct ec_response_get_cmd_versions *rver; | |
293 | struct cros_ec_command *msg; | |
294 | int ret; | |
295 | ||
296 | msg = kmalloc(sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)), | |
297 | GFP_KERNEL); | |
298 | if (!msg) | |
299 | return -ENOMEM; | |
300 | ||
301 | msg->version = 0; | |
302 | msg->command = EC_CMD_GET_CMD_VERSIONS; | |
303 | msg->insize = sizeof(*rver); | |
304 | msg->outsize = sizeof(*pver); | |
305 | ||
306 | pver = (struct ec_params_get_cmd_versions *)msg->data; | |
307 | pver->cmd = cmd; | |
308 | ||
d4da97e5 | 309 | ret = send_command(ec_dev, msg); |
6f1d912b VY |
310 | if (ret > 0) { |
311 | rver = (struct ec_response_get_cmd_versions *)msg->data; | |
312 | *mask = rver->version_mask; | |
313 | } | |
314 | ||
315 | kfree(msg); | |
316 | ||
317 | return ret; | |
318 | } | |
319 | ||
2c7589af SB |
320 | int cros_ec_query_all(struct cros_ec_device *ec_dev) |
321 | { | |
322 | struct device *dev = ec_dev->dev; | |
323 | struct cros_ec_command *proto_msg; | |
324 | struct ec_response_get_protocol_info *proto_info; | |
6f1d912b | 325 | u32 ver_mask = 0; |
2c7589af SB |
326 | int ret; |
327 | ||
328 | proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info), | |
329 | GFP_KERNEL); | |
330 | if (!proto_msg) | |
331 | return -ENOMEM; | |
332 | ||
333 | /* First try sending with proto v3. */ | |
334 | ec_dev->proto_version = 3; | |
335 | ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg); | |
336 | ||
337 | if (ret == 0) { | |
338 | proto_info = (struct ec_response_get_protocol_info *) | |
339 | proto_msg->data; | |
340 | ec_dev->max_request = proto_info->max_request_packet_size - | |
341 | sizeof(struct ec_host_request); | |
342 | ec_dev->max_response = proto_info->max_response_packet_size - | |
343 | sizeof(struct ec_host_response); | |
344 | ec_dev->proto_version = | |
345 | min(EC_HOST_REQUEST_VERSION, | |
346 | fls(proto_info->protocol_versions) - 1); | |
347 | dev_dbg(ec_dev->dev, | |
348 | "using proto v%u\n", | |
349 | ec_dev->proto_version); | |
350 | ||
351 | ec_dev->din_size = ec_dev->max_response + | |
352 | sizeof(struct ec_host_response) + | |
353 | EC_MAX_RESPONSE_OVERHEAD; | |
354 | ec_dev->dout_size = ec_dev->max_request + | |
355 | sizeof(struct ec_host_request) + | |
356 | EC_MAX_REQUEST_OVERHEAD; | |
062476f2 JMC |
357 | |
358 | /* | |
2c7589af | 359 | * Check for PD |
062476f2 | 360 | */ |
2c7589af | 361 | ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg); |
062476f2 | 362 | |
2c7589af SB |
363 | if (ret) { |
364 | dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret); | |
365 | ec_dev->max_passthru = 0; | |
366 | } else { | |
367 | dev_dbg(ec_dev->dev, "found PD chip\n"); | |
368 | ec_dev->max_passthru = | |
369 | proto_info->max_request_packet_size - | |
370 | sizeof(struct ec_host_request); | |
371 | } | |
372 | } else { | |
373 | /* Try querying with a v2 hello message. */ | |
374 | ec_dev->proto_version = 2; | |
375 | ret = cros_ec_host_command_proto_query_v2(ec_dev); | |
062476f2 | 376 | |
2c7589af SB |
377 | if (ret == 0) { |
378 | /* V2 hello succeeded. */ | |
379 | dev_dbg(ec_dev->dev, "falling back to proto v2\n"); | |
062476f2 | 380 | |
2c7589af SB |
381 | ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE; |
382 | ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE; | |
383 | ec_dev->max_passthru = 0; | |
384 | ec_dev->pkt_xfer = NULL; | |
5d749d0b GG |
385 | ec_dev->din_size = EC_PROTO2_MSG_BYTES; |
386 | ec_dev->dout_size = EC_PROTO2_MSG_BYTES; | |
2c7589af SB |
387 | } else { |
388 | /* | |
389 | * It's possible for a test to occur too early when | |
390 | * the EC isn't listening. If this happens, we'll | |
391 | * test later when the first command is run. | |
392 | */ | |
393 | ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN; | |
394 | dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret); | |
395 | goto exit; | |
062476f2 | 396 | } |
2c7589af | 397 | } |
062476f2 | 398 | |
2c7589af SB |
399 | devm_kfree(dev, ec_dev->din); |
400 | devm_kfree(dev, ec_dev->dout); | |
401 | ||
402 | ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); | |
403 | if (!ec_dev->din) { | |
404 | ret = -ENOMEM; | |
405 | goto exit; | |
062476f2 | 406 | } |
2c7589af SB |
407 | |
408 | ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); | |
409 | if (!ec_dev->dout) { | |
410 | devm_kfree(dev, ec_dev->din); | |
411 | ret = -ENOMEM; | |
412 | goto exit; | |
413 | } | |
414 | ||
6f1d912b VY |
415 | /* Probe if MKBP event is supported */ |
416 | ret = cros_ec_get_host_command_version_mask(ec_dev, | |
417 | EC_CMD_GET_NEXT_EVENT, | |
418 | &ver_mask); | |
419 | if (ret < 0 || ver_mask == 0) | |
420 | ec_dev->mkbp_event_supported = 0; | |
421 | else | |
422 | ec_dev->mkbp_event_supported = 1; | |
423 | ||
29d99b96 SN |
424 | /* |
425 | * Get host event wake mask, assume all events are wake events | |
426 | * if unavailable. | |
427 | */ | |
428 | ret = cros_ec_get_host_event_wake_mask(ec_dev, proto_msg, | |
429 | &ec_dev->host_event_wake_mask); | |
430 | if (ret < 0) | |
431 | ec_dev->host_event_wake_mask = U32_MAX; | |
432 | ||
a27b8f31 TE |
433 | ret = 0; |
434 | ||
062476f2 | 435 | exit: |
2c7589af SB |
436 | kfree(proto_msg); |
437 | return ret; | |
438 | } | |
439 | EXPORT_SYMBOL(cros_ec_query_all); | |
440 | ||
441 | int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, | |
442 | struct cros_ec_command *msg) | |
443 | { | |
444 | int ret; | |
445 | ||
446 | mutex_lock(&ec_dev->lock); | |
447 | if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) { | |
448 | ret = cros_ec_query_all(ec_dev); | |
449 | if (ret) { | |
450 | dev_err(ec_dev->dev, | |
451 | "EC version unknown and query failed; aborting command\n"); | |
452 | mutex_unlock(&ec_dev->lock); | |
453 | return ret; | |
454 | } | |
455 | } | |
456 | ||
457 | if (msg->insize > ec_dev->max_response) { | |
458 | dev_dbg(ec_dev->dev, "clamping message receive buffer\n"); | |
459 | msg->insize = ec_dev->max_response; | |
460 | } | |
461 | ||
462 | if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) { | |
463 | if (msg->outsize > ec_dev->max_request) { | |
464 | dev_err(ec_dev->dev, | |
465 | "request of size %u is too big (max: %u)\n", | |
466 | msg->outsize, | |
467 | ec_dev->max_request); | |
468 | mutex_unlock(&ec_dev->lock); | |
469 | return -EMSGSIZE; | |
470 | } | |
471 | } else { | |
472 | if (msg->outsize > ec_dev->max_passthru) { | |
473 | dev_err(ec_dev->dev, | |
474 | "passthru rq of size %u is too big (max: %u)\n", | |
475 | msg->outsize, | |
476 | ec_dev->max_passthru); | |
477 | mutex_unlock(&ec_dev->lock); | |
478 | return -EMSGSIZE; | |
479 | } | |
480 | } | |
481 | ret = send_command(ec_dev, msg); | |
062476f2 JMC |
482 | mutex_unlock(&ec_dev->lock); |
483 | ||
484 | return ret; | |
485 | } | |
486 | EXPORT_SYMBOL(cros_ec_cmd_xfer); | |
9798ac6d TV |
487 | |
488 | int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, | |
489 | struct cros_ec_command *msg) | |
490 | { | |
491 | int ret; | |
492 | ||
493 | ret = cros_ec_cmd_xfer(ec_dev, msg); | |
494 | if (ret < 0) { | |
495 | dev_err(ec_dev->dev, "Command xfer error (err:%d)\n", ret); | |
496 | } else if (msg->result != EC_RES_SUCCESS) { | |
497 | dev_dbg(ec_dev->dev, "Command result (err: %d)\n", msg->result); | |
498 | return -EPROTO; | |
499 | } | |
500 | ||
501 | return ret; | |
502 | } | |
503 | EXPORT_SYMBOL(cros_ec_cmd_xfer_status); | |
6f1d912b VY |
504 | |
505 | static int get_next_event(struct cros_ec_device *ec_dev) | |
506 | { | |
507 | u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)]; | |
508 | struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; | |
509 | int ret; | |
510 | ||
a9eb186e JL |
511 | if (ec_dev->suspended) { |
512 | dev_dbg(ec_dev->dev, "Device suspended.\n"); | |
513 | return -EHOSTDOWN; | |
514 | } | |
515 | ||
6f1d912b VY |
516 | msg->version = 0; |
517 | msg->command = EC_CMD_GET_NEXT_EVENT; | |
518 | msg->insize = sizeof(ec_dev->event_data); | |
519 | msg->outsize = 0; | |
520 | ||
521 | ret = cros_ec_cmd_xfer(ec_dev, msg); | |
522 | if (ret > 0) { | |
523 | ec_dev->event_size = ret - 1; | |
524 | memcpy(&ec_dev->event_data, msg->data, | |
525 | sizeof(ec_dev->event_data)); | |
526 | } | |
527 | ||
528 | return ret; | |
529 | } | |
530 | ||
531 | static int get_keyboard_state_event(struct cros_ec_device *ec_dev) | |
532 | { | |
533 | u8 buffer[sizeof(struct cros_ec_command) + | |
534 | sizeof(ec_dev->event_data.data)]; | |
535 | struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; | |
536 | ||
537 | msg->version = 0; | |
538 | msg->command = EC_CMD_MKBP_STATE; | |
539 | msg->insize = sizeof(ec_dev->event_data.data); | |
540 | msg->outsize = 0; | |
541 | ||
542 | ec_dev->event_size = cros_ec_cmd_xfer(ec_dev, msg); | |
543 | ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX; | |
544 | memcpy(&ec_dev->event_data.data, msg->data, | |
545 | sizeof(ec_dev->event_data.data)); | |
546 | ||
547 | return ec_dev->event_size; | |
548 | } | |
549 | ||
29d99b96 | 550 | int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event) |
6f1d912b | 551 | { |
29d99b96 SN |
552 | u32 host_event; |
553 | int ret; | |
554 | ||
555 | if (!ec_dev->mkbp_event_supported) { | |
556 | ret = get_keyboard_state_event(ec_dev); | |
557 | if (ret < 0) | |
558 | return ret; | |
559 | ||
560 | if (wake_event) | |
561 | *wake_event = true; | |
562 | ||
563 | return ret; | |
564 | } | |
565 | ||
566 | ret = get_next_event(ec_dev); | |
567 | if (ret < 0) | |
568 | return ret; | |
569 | ||
570 | if (wake_event) { | |
571 | host_event = cros_ec_get_host_event(ec_dev); | |
572 | ||
573 | /* Consider non-host_event as wake event */ | |
574 | *wake_event = !host_event || | |
575 | !!(host_event & ec_dev->host_event_wake_mask); | |
576 | } | |
577 | ||
578 | return ret; | |
6f1d912b VY |
579 | } |
580 | EXPORT_SYMBOL(cros_ec_get_next_event); | |
68c35ea2 GG |
581 | |
582 | u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev) | |
583 | { | |
584 | u32 host_event; | |
585 | ||
586 | BUG_ON(!ec_dev->mkbp_event_supported); | |
587 | ||
588 | if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT) | |
589 | return 0; | |
590 | ||
591 | if (ec_dev->event_size != sizeof(host_event)) { | |
592 | dev_warn(ec_dev->dev, "Invalid host event size\n"); | |
593 | return 0; | |
594 | } | |
595 | ||
596 | host_event = get_unaligned_le32(&ec_dev->event_data.data.host_event); | |
597 | ||
598 | return host_event; | |
599 | } | |
600 | EXPORT_SYMBOL(cros_ec_get_host_event); |