]>
Commit | Line | Data |
---|---|---|
fb7d879f OW |
1 | /* |
2 | * | |
3 | * Intel Management Engine Interface (Intel MEI) Linux driver | |
733ba91c | 4 | * Copyright (c) 2003-2012, Intel Corporation. |
fb7d879f OW |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | */ | |
16 | ||
17 | ||
40e0b67b | 18 | #include <linux/export.h> |
fb7d879f OW |
19 | #include <linux/kthread.h> |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/fs.h> | |
22 | #include <linux/jiffies.h> | |
1f180359 | 23 | #include <linux/slab.h> |
34af1913 | 24 | #include <linux/pm_runtime.h> |
fb7d879f | 25 | |
4f3afe1d | 26 | #include <linux/mei.h> |
47a73801 TW |
27 | |
28 | #include "mei_dev.h" | |
0edb23fc | 29 | #include "hbm.h" |
90e0b5f1 | 30 | #include "client.h" |
fb7d879f OW |
31 | |
32 | ||
4c6e22b8 | 33 | /** |
83ce0741 | 34 | * mei_irq_compl_handler - dispatch complete handlers |
4c6e22b8 TW |
35 | * for the completed callbacks |
36 | * | |
a8605ea2 AU |
37 | * @dev: mei device |
38 | * @compl_list: list of completed cbs | |
4c6e22b8 TW |
39 | */ |
40 | void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) | |
41 | { | |
42 | struct mei_cl_cb *cb, *next; | |
43 | struct mei_cl *cl; | |
44 | ||
45 | list_for_each_entry_safe(cb, next, &compl_list->list, list) { | |
46 | cl = cb->cl; | |
c54bf3ab | 47 | list_del_init(&cb->list); |
4c6e22b8 | 48 | |
2bf94cab | 49 | dev_dbg(dev->dev, "completing call back.\n"); |
4c6e22b8 | 50 | if (cl == &dev->iamthif_cl) |
9abd8b31 | 51 | mei_amthif_complete(cl, cb); |
4c6e22b8 | 52 | else |
db086fa9 | 53 | mei_cl_complete(cl, cb); |
4c6e22b8 TW |
54 | } |
55 | } | |
40e0b67b | 56 | EXPORT_SYMBOL_GPL(mei_irq_compl_handler); |
6e0f180f | 57 | |
fb7d879f | 58 | /** |
6e0f180f | 59 | * mei_cl_hbm_equal - check if hbm is addressed to the client |
fb7d879f | 60 | * |
6e0f180f | 61 | * @cl: host client |
fb7d879f OW |
62 | * @mei_hdr: header of mei client message |
63 | * | |
a8605ea2 | 64 | * Return: true if matches, false otherwise |
fb7d879f | 65 | */ |
6e0f180f TW |
66 | static inline int mei_cl_hbm_equal(struct mei_cl *cl, |
67 | struct mei_msg_hdr *mei_hdr) | |
fb7d879f | 68 | { |
1df629ef | 69 | return mei_cl_host_addr(cl) == mei_hdr->host_addr && |
d49ed64a | 70 | mei_cl_me_id(cl) == mei_hdr->me_addr; |
6e0f180f | 71 | } |
fb7d879f | 72 | |
331e4187 TW |
73 | /** |
74 | * mei_irq_discard_msg - discard received message | |
75 | * | |
76 | * @dev: mei device | |
77 | * @hdr: message header | |
78 | */ | |
331e4187 TW |
79 | void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) |
80 | { | |
81 | /* | |
82 | * no need to check for size as it is guarantied | |
83 | * that length fits into rd_msg_buf | |
84 | */ | |
85 | mei_read_slots(dev, dev->rd_msg_buf, hdr->length); | |
86 | dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", | |
87 | MEI_HDR_PRM(hdr)); | |
88 | } | |
89 | ||
fb7d879f | 90 | /** |
ce23139c | 91 | * mei_cl_irq_read_msg - process client message |
fb7d879f | 92 | * |
3d33ff24 | 93 | * @cl: reading client |
fb7d879f | 94 | * @mei_hdr: header of mei client message |
3d33ff24 | 95 | * @complete_list: completion list |
fb7d879f | 96 | * |
3d33ff24 | 97 | * Return: always 0 |
fb7d879f | 98 | */ |
331e4187 TW |
99 | int mei_cl_irq_read_msg(struct mei_cl *cl, |
100 | struct mei_msg_hdr *mei_hdr, | |
101 | struct mei_cl_cb *complete_list) | |
fb7d879f | 102 | { |
3d33ff24 TW |
103 | struct mei_device *dev = cl->dev; |
104 | struct mei_cl_cb *cb; | |
f862b6b2 | 105 | size_t buf_sz; |
fb7d879f | 106 | |
a9bed610 TW |
107 | cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); |
108 | if (!cb) { | |
a808c80c AU |
109 | if (!mei_cl_is_fixed_address(cl)) { |
110 | cl_err(dev, cl, "pending read cb not found\n"); | |
dfe5f753 | 111 | goto discard; |
a808c80c AU |
112 | } |
113 | cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, cl->fp); | |
114 | if (!cb) | |
dfe5f753 | 115 | goto discard; |
a808c80c | 116 | list_add_tail(&cb->list, &cl->rd_pending); |
3d33ff24 | 117 | } |
6e0f180f | 118 | |
f3de9b63 | 119 | if (!mei_cl_is_connected(cl)) { |
a9bed610 | 120 | cl_dbg(dev, cl, "not connected\n"); |
a03c608f | 121 | list_move_tail(&cb->list, &complete_list->list); |
a9bed610 | 122 | cb->status = -ENODEV; |
dfe5f753 | 123 | goto discard; |
3d33ff24 | 124 | } |
6e0f180f | 125 | |
f862b6b2 TW |
126 | buf_sz = mei_hdr->length + cb->buf_idx; |
127 | /* catch for integer overflow */ | |
128 | if (buf_sz < cb->buf_idx) { | |
35bf7692 | 129 | cl_err(dev, cl, "message is too big len %d idx %zu\n", |
f862b6b2 TW |
130 | mei_hdr->length, cb->buf_idx); |
131 | ||
132 | list_move_tail(&cb->list, &complete_list->list); | |
133 | cb->status = -EMSGSIZE; | |
dfe5f753 | 134 | goto discard; |
f862b6b2 TW |
135 | } |
136 | ||
137 | if (cb->buf.size < buf_sz) { | |
35bf7692 | 138 | cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", |
5db7514d | 139 | cb->buf.size, mei_hdr->length, cb->buf_idx); |
3d33ff24 | 140 | |
dfe5f753 AU |
141 | list_move_tail(&cb->list, &complete_list->list); |
142 | cb->status = -EMSGSIZE; | |
143 | goto discard; | |
fb7d879f OW |
144 | } |
145 | ||
dfe5f753 | 146 | mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length); |
3d33ff24 TW |
147 | |
148 | cb->buf_idx += mei_hdr->length; | |
331e4187 | 149 | |
3d33ff24 | 150 | if (mei_hdr->msg_complete) { |
35bf7692 | 151 | cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx); |
3d33ff24 | 152 | list_move_tail(&cb->list, &complete_list->list); |
34af1913 AU |
153 | } else { |
154 | pm_runtime_mark_last_busy(dev->dev); | |
155 | pm_request_autosuspend(dev->dev); | |
3d33ff24 TW |
156 | } |
157 | ||
dfe5f753 | 158 | return 0; |
fb7d879f | 159 | |
dfe5f753 AU |
160 | discard: |
161 | mei_irq_discard_msg(dev, mei_hdr); | |
fb7d879f OW |
162 | return 0; |
163 | } | |
164 | ||
6bb948c9 TW |
165 | /** |
166 | * mei_cl_irq_disconnect_rsp - send disconnection response message | |
167 | * | |
168 | * @cl: client | |
169 | * @cb: callback block. | |
6bb948c9 TW |
170 | * @cmpl_list: complete list. |
171 | * | |
a8605ea2 | 172 | * Return: 0, OK; otherwise, error. |
6bb948c9 TW |
173 | */ |
174 | static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, | |
9d098192 | 175 | struct mei_cl_cb *cmpl_list) |
6bb948c9 TW |
176 | { |
177 | struct mei_device *dev = cl->dev; | |
9d098192 TW |
178 | u32 msg_slots; |
179 | int slots; | |
6bb948c9 TW |
180 | int ret; |
181 | ||
9d098192 TW |
182 | slots = mei_hbuf_empty_slots(dev); |
183 | msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_response)); | |
6bb948c9 | 184 | |
9d098192 | 185 | if (slots < msg_slots) |
6bb948c9 TW |
186 | return -EMSGSIZE; |
187 | ||
6bb948c9 | 188 | ret = mei_hbm_cl_disconnect_rsp(dev, cl); |
6a8d648c | 189 | list_move_tail(&cb->list, &cmpl_list->list); |
6bb948c9 TW |
190 | |
191 | return ret; | |
192 | } | |
193 | ||
fb7d879f | 194 | /** |
ce23139c | 195 | * mei_cl_irq_read - processes client read related operation from the |
6220d6a0 | 196 | * interrupt thread context - request for flow control credits |
fb7d879f | 197 | * |
6220d6a0 TW |
198 | * @cl: client |
199 | * @cb: callback block. | |
fb7d879f OW |
200 | * @cmpl_list: complete list. |
201 | * | |
a8605ea2 | 202 | * Return: 0, OK; otherwise, error. |
fb7d879f | 203 | */ |
6220d6a0 | 204 | static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, |
9d098192 | 205 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 206 | { |
6220d6a0 | 207 | struct mei_device *dev = cl->dev; |
9d098192 TW |
208 | u32 msg_slots; |
209 | int slots; | |
2ebf8c94 TW |
210 | int ret; |
211 | ||
ff1586a7 AU |
212 | if (!list_empty(&cl->rd_pending)) |
213 | return 0; | |
214 | ||
9d098192 TW |
215 | msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); |
216 | slots = mei_hbuf_empty_slots(dev); | |
2ebf8c94 | 217 | |
9d098192 | 218 | if (slots < msg_slots) |
c8c8d080 | 219 | return -EMSGSIZE; |
7bdf72d3 | 220 | |
2ebf8c94 TW |
221 | ret = mei_hbm_cl_flow_control_req(dev, cl); |
222 | if (ret) { | |
223 | cl->status = ret; | |
6220d6a0 TW |
224 | cb->buf_idx = 0; |
225 | list_move_tail(&cb->list, &cmpl_list->list); | |
2ebf8c94 | 226 | return ret; |
1ccb7b62 | 227 | } |
2ebf8c94 | 228 | |
a9bed610 | 229 | list_move_tail(&cb->list, &cl->rd_pending); |
1ccb7b62 | 230 | |
fb7d879f OW |
231 | return 0; |
232 | } | |
233 | ||
603c53e4 AU |
234 | static inline bool hdr_is_hbm(struct mei_msg_hdr *mei_hdr) |
235 | { | |
236 | return mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0; | |
237 | } | |
238 | ||
239 | static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr) | |
240 | { | |
241 | return mei_hdr->host_addr == 0 && mei_hdr->me_addr != 0; | |
242 | } | |
243 | ||
fb7d879f | 244 | /** |
393b148f | 245 | * mei_irq_read_handler - bottom half read routine after ISR to |
fb7d879f OW |
246 | * handle the read processing. |
247 | * | |
fb7d879f | 248 | * @dev: the device structure |
06ecd645 | 249 | * @cmpl_list: An instance of our list structure |
fb7d879f OW |
250 | * @slots: slots to read. |
251 | * | |
a8605ea2 | 252 | * Return: 0 on success, <0 on failure. |
fb7d879f | 253 | */ |
06ecd645 TW |
254 | int mei_irq_read_handler(struct mei_device *dev, |
255 | struct mei_cl_cb *cmpl_list, s32 *slots) | |
fb7d879f OW |
256 | { |
257 | struct mei_msg_hdr *mei_hdr; | |
10ee9074 TW |
258 | struct mei_cl *cl; |
259 | int ret; | |
fb7d879f OW |
260 | |
261 | if (!dev->rd_msg_hdr) { | |
827eef51 | 262 | dev->rd_msg_hdr = mei_read_hdr(dev); |
fb7d879f | 263 | (*slots)--; |
2bf94cab | 264 | dev_dbg(dev->dev, "slots =%08x.\n", *slots); |
fb7d879f OW |
265 | } |
266 | mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; | |
2bf94cab | 267 | dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); |
fb7d879f OW |
268 | |
269 | if (mei_hdr->reserved || !dev->rd_msg_hdr) { | |
2bf94cab | 270 | dev_err(dev->dev, "corrupted message header 0x%08X\n", |
10ee9074 | 271 | dev->rd_msg_hdr); |
fb7d879f OW |
272 | ret = -EBADMSG; |
273 | goto end; | |
274 | } | |
275 | ||
10ee9074 | 276 | if (mei_slots2data(*slots) < mei_hdr->length) { |
2bf94cab | 277 | dev_err(dev->dev, "less data available than length=%08x.\n", |
fb7d879f OW |
278 | *slots); |
279 | /* we can't read the message */ | |
b1b94b5d | 280 | ret = -ENODATA; |
fb7d879f OW |
281 | goto end; |
282 | } | |
283 | ||
10ee9074 | 284 | /* HBM message */ |
603c53e4 | 285 | if (hdr_is_hbm(mei_hdr)) { |
544f9460 TW |
286 | ret = mei_hbm_dispatch(dev, mei_hdr); |
287 | if (ret) { | |
2bf94cab | 288 | dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n", |
544f9460 TW |
289 | ret); |
290 | goto end; | |
291 | } | |
10ee9074 TW |
292 | goto reset_slots; |
293 | } | |
15d4acc5 | 294 | |
83ce0741 | 295 | /* find recipient cl */ |
10ee9074 TW |
296 | list_for_each_entry(cl, &dev->file_list, link) { |
297 | if (mei_cl_hbm_equal(cl, mei_hdr)) { | |
298 | cl_dbg(dev, cl, "got a message\n"); | |
299 | break; | |
300 | } | |
301 | } | |
302 | ||
83ce0741 | 303 | /* if no recipient cl was found we assume corrupted header */ |
10ee9074 | 304 | if (&cl->link == &dev->file_list) { |
603c53e4 AU |
305 | /* A message for not connected fixed address clients |
306 | * should be silently discarded | |
307 | */ | |
308 | if (hdr_is_fixed(mei_hdr)) { | |
309 | mei_irq_discard_msg(dev, mei_hdr); | |
310 | ret = 0; | |
311 | goto reset_slots; | |
312 | } | |
2bf94cab | 313 | dev_err(dev->dev, "no destination client found 0x%08X\n", |
10ee9074 TW |
314 | dev->rd_msg_hdr); |
315 | ret = -EBADMSG; | |
316 | goto end; | |
317 | } | |
318 | ||
db4756fd TW |
319 | if (cl == &dev->iamthif_cl) { |
320 | ret = mei_amthif_irq_read_msg(cl, mei_hdr, cmpl_list); | |
fb7d879f | 321 | } else { |
3d33ff24 | 322 | ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); |
fb7d879f OW |
323 | } |
324 | ||
3d33ff24 | 325 | |
10ee9074 | 326 | reset_slots: |
fb7d879f OW |
327 | /* reset the number of slots and header */ |
328 | *slots = mei_count_full_read_slots(dev); | |
329 | dev->rd_msg_hdr = 0; | |
330 | ||
331 | if (*slots == -EOVERFLOW) { | |
332 | /* overflow - reset */ | |
2bf94cab | 333 | dev_err(dev->dev, "resetting due to slots overflow.\n"); |
fb7d879f OW |
334 | /* set the event since message has been read */ |
335 | ret = -ERANGE; | |
336 | goto end; | |
337 | } | |
338 | end: | |
339 | return ret; | |
340 | } | |
40e0b67b | 341 | EXPORT_SYMBOL_GPL(mei_irq_read_handler); |
fb7d879f OW |
342 | |
343 | ||
344 | /** | |
06ecd645 TW |
345 | * mei_irq_write_handler - dispatch write requests |
346 | * after irq received | |
fb7d879f | 347 | * |
fb7d879f | 348 | * @dev: the device structure |
9a84d616 | 349 | * @cmpl_list: An instance of our list structure |
fb7d879f | 350 | * |
a8605ea2 | 351 | * Return: 0 on success, <0 on failure. |
fb7d879f | 352 | */ |
c8c8d080 | 353 | int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) |
fb7d879f OW |
354 | { |
355 | ||
356 | struct mei_cl *cl; | |
6220d6a0 | 357 | struct mei_cl_cb *cb, *next; |
fb601adb | 358 | struct mei_cl_cb *list; |
9a84d616 | 359 | s32 slots; |
fb7d879f OW |
360 | int ret; |
361 | ||
6aae48ff TW |
362 | |
363 | if (!mei_hbuf_acquire(dev)) | |
fb7d879f | 364 | return 0; |
6aae48ff | 365 | |
9a84d616 TW |
366 | slots = mei_hbuf_empty_slots(dev); |
367 | if (slots <= 0) | |
7d5e0e59 TW |
368 | return -EMSGSIZE; |
369 | ||
fb7d879f | 370 | /* complete all waiting for write CB */ |
2bf94cab | 371 | dev_dbg(dev->dev, "complete all waiting for write cb.\n"); |
fb7d879f OW |
372 | |
373 | list = &dev->write_waiting_list; | |
6220d6a0 TW |
374 | list_for_each_entry_safe(cb, next, &list->list, list) { |
375 | cl = cb->cl; | |
b7cd2d9f TW |
376 | |
377 | cl->status = 0; | |
c54bf3ab TW |
378 | cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); |
379 | cl->writing_state = MEI_WRITE_COMPLETE; | |
380 | list_move_tail(&cb->list, &cmpl_list->list); | |
fb7d879f OW |
381 | } |
382 | ||
fb7d879f | 383 | /* complete control write list CB */ |
2bf94cab | 384 | dev_dbg(dev->dev, "complete control write list cb.\n"); |
6220d6a0 TW |
385 | list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { |
386 | cl = cb->cl; | |
6220d6a0 | 387 | switch (cb->fop_type) { |
5a8373fb | 388 | case MEI_FOP_DISCONNECT: |
c8372094 | 389 | /* send disconnect message */ |
5a8373fb | 390 | ret = mei_cl_irq_disconnect(cl, cb, cmpl_list); |
c8372094 TW |
391 | if (ret) |
392 | return ret; | |
fb7d879f | 393 | |
c8372094 | 394 | break; |
4b8960b4 | 395 | case MEI_FOP_READ: |
c8372094 | 396 | /* send flow control message */ |
9d098192 | 397 | ret = mei_cl_irq_read(cl, cb, cmpl_list); |
c8372094 TW |
398 | if (ret) |
399 | return ret; | |
fb7d879f | 400 | |
c8372094 | 401 | break; |
02a7eecc | 402 | case MEI_FOP_CONNECT: |
c8372094 | 403 | /* connect message */ |
9d098192 | 404 | ret = mei_cl_irq_connect(cl, cb, cmpl_list); |
c8372094 TW |
405 | if (ret) |
406 | return ret; | |
fb7d879f | 407 | |
c8372094 | 408 | break; |
6bb948c9 TW |
409 | case MEI_FOP_DISCONNECT_RSP: |
410 | /* send disconnect resp */ | |
9d098192 | 411 | ret = mei_cl_irq_disconnect_rsp(cl, cb, cmpl_list); |
6bb948c9 TW |
412 | if (ret) |
413 | return ret; | |
31a5ef24 | 414 | break; |
51678ccb TW |
415 | |
416 | case MEI_FOP_NOTIFY_START: | |
417 | case MEI_FOP_NOTIFY_STOP: | |
418 | ret = mei_cl_irq_notify(cl, cb, cmpl_list); | |
419 | if (ret) | |
420 | return ret; | |
421 | break; | |
c8372094 TW |
422 | default: |
423 | BUG(); | |
fb7d879f | 424 | } |
c8372094 | 425 | |
fb7d879f OW |
426 | } |
427 | /* complete write list CB */ | |
2bf94cab | 428 | dev_dbg(dev->dev, "complete write list cb.\n"); |
6220d6a0 TW |
429 | list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { |
430 | cl = cb->cl; | |
be9d87a7 | 431 | if (cl == &dev->iamthif_cl) |
9d098192 | 432 | ret = mei_amthif_irq_write(cl, cb, cmpl_list); |
be9d87a7 | 433 | else |
9d098192 | 434 | ret = mei_cl_irq_write(cl, cb, cmpl_list); |
be9d87a7 TW |
435 | if (ret) |
436 | return ret; | |
fb7d879f OW |
437 | } |
438 | return 0; | |
439 | } | |
40e0b67b | 440 | EXPORT_SYMBOL_GPL(mei_irq_write_handler); |
fb7d879f OW |
441 | |
442 | ||
18901357 AU |
443 | /** |
444 | * mei_connect_timeout - connect/disconnect timeouts | |
445 | * | |
446 | * @cl: host client | |
447 | */ | |
448 | static void mei_connect_timeout(struct mei_cl *cl) | |
449 | { | |
450 | struct mei_device *dev = cl->dev; | |
451 | ||
452 | if (cl->state == MEI_FILE_CONNECTING) { | |
453 | if (dev->hbm_f_dot_supported) { | |
454 | cl->state = MEI_FILE_DISCONNECT_REQUIRED; | |
455 | wake_up(&cl->wait); | |
456 | return; | |
457 | } | |
458 | } | |
459 | mei_reset(dev); | |
460 | } | |
fb7d879f | 461 | |
1892fc2e AU |
462 | #define MEI_STALL_TIMER_FREQ (2 * HZ) |
463 | /** | |
464 | * mei_schedule_stall_timer - re-arm stall_timer work | |
465 | * | |
466 | * Schedule stall timer | |
467 | * | |
468 | * @dev: the device structure | |
469 | */ | |
470 | void mei_schedule_stall_timer(struct mei_device *dev) | |
471 | { | |
472 | schedule_delayed_work(&dev->timer_work, MEI_STALL_TIMER_FREQ); | |
473 | } | |
474 | ||
fb7d879f OW |
475 | /** |
476 | * mei_timer - timer function. | |
477 | * | |
478 | * @work: pointer to the work_struct structure | |
479 | * | |
fb7d879f | 480 | */ |
a61c6530 | 481 | void mei_timer(struct work_struct *work) |
fb7d879f | 482 | { |
31f88f57 | 483 | struct mei_cl *cl; |
fb7d879f | 484 | struct mei_device *dev = container_of(work, |
a61c6530 | 485 | struct mei_device, timer_work.work); |
1892fc2e | 486 | bool reschedule_timer = false; |
fb7d879f OW |
487 | |
488 | mutex_lock(&dev->device_lock); | |
66ae460b TW |
489 | |
490 | /* Catch interrupt stalls during HBM init handshake */ | |
491 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS && | |
492 | dev->hbm_state != MEI_HBM_IDLE) { | |
493 | ||
494 | if (dev->init_clients_timer) { | |
495 | if (--dev->init_clients_timer == 0) { | |
2bf94cab | 496 | dev_err(dev->dev, "timer: init clients timeout hbm_state = %d.\n", |
66ae460b | 497 | dev->hbm_state); |
33ec0826 | 498 | mei_reset(dev); |
66ae460b | 499 | goto out; |
fb7d879f | 500 | } |
1892fc2e | 501 | reschedule_timer = true; |
fb7d879f | 502 | } |
fb7d879f | 503 | } |
66ae460b TW |
504 | |
505 | if (dev->dev_state != MEI_DEV_ENABLED) | |
506 | goto out; | |
507 | ||
fb7d879f | 508 | /*** connect/disconnect timeouts ***/ |
31f88f57 TW |
509 | list_for_each_entry(cl, &dev->file_list, link) { |
510 | if (cl->timer_count) { | |
511 | if (--cl->timer_count == 0) { | |
2bf94cab | 512 | dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); |
18901357 | 513 | mei_connect_timeout(cl); |
fb7d879f OW |
514 | goto out; |
515 | } | |
1892fc2e | 516 | reschedule_timer = true; |
fb7d879f OW |
517 | } |
518 | } | |
519 | ||
64092858 TW |
520 | if (!mei_cl_is_connected(&dev->iamthif_cl)) |
521 | goto out; | |
522 | ||
fb7d879f OW |
523 | if (dev->iamthif_stall_timer) { |
524 | if (--dev->iamthif_stall_timer == 0) { | |
2bf94cab | 525 | dev_err(dev->dev, "timer: amthif hanged.\n"); |
33ec0826 | 526 | mei_reset(dev); |
fb7d879f | 527 | |
19838fb8 | 528 | mei_amthif_run_next_cmd(dev); |
1892fc2e | 529 | goto out; |
fb7d879f | 530 | } |
1892fc2e | 531 | reschedule_timer = true; |
fb7d879f OW |
532 | } |
533 | ||
fb7d879f | 534 | out: |
1892fc2e AU |
535 | if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer) |
536 | mei_schedule_stall_timer(dev); | |
537 | ||
441ab50f | 538 | mutex_unlock(&dev->device_lock); |
fb7d879f | 539 | } |