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