interrupt.c 17.4 KB
Newer Older
O
Oren Weil 已提交
1 2 3
/*
 *
 * Intel Management Engine Interface (Intel MEI) Linux driver
4
 * Copyright (c) 2003-2012, Intel Corporation.
O
Oren Weil 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */


18
#include <linux/export.h>
O
Oren Weil 已提交
19 20 21 22 23 24
#include <linux/pci.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/jiffies.h>

25
#include <linux/mei.h>
26 27

#include "mei_dev.h"
28
#include "hbm.h"
T
Tomas Winkler 已提交
29
#include "hw-me.h"
T
Tomas Winkler 已提交
30
#include "client.h"
O
Oren Weil 已提交
31 32 33


/**
34
 * mei_cl_complete_handler - processes completed operation for a client
O
Oren Weil 已提交
35 36
 *
 * @cl: private data of the file object.
37
 * @cb: callback block.
O
Oren Weil 已提交
38
 */
39
static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb)
O
Oren Weil 已提交
40
{
41 42 43
	if (cb->fop_type == MEI_FOP_WRITE) {
		mei_io_cb_free(cb);
		cb = NULL;
O
Oren Weil 已提交
44 45 46 47
		cl->writing_state = MEI_WRITE_COMPLETE;
		if (waitqueue_active(&cl->tx_wait))
			wake_up_interruptible(&cl->tx_wait);

48
	} else if (cb->fop_type == MEI_FOP_READ &&
O
Oren Weil 已提交
49 50 51 52
			MEI_READING == cl->reading_state) {
		cl->reading_state = MEI_READ_COMPLETE;
		if (waitqueue_active(&cl->rx_wait))
			wake_up_interruptible(&cl->rx_wait);
53 54
		else
			mei_cl_bus_rx_event(cl);
O
Oren Weil 已提交
55 56 57 58

	}
}

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/**
 * mei_irq_compl_handler - dispatch complete handelers
 *	for the completed callbacks
 *
 * @dev - mei device
 * @compl_list - list of completed cbs
 */
void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list)
{
	struct mei_cl_cb *cb, *next;
	struct mei_cl *cl;

	list_for_each_entry_safe(cb, next, &compl_list->list, list) {
		cl = cb->cl;
		list_del(&cb->list);
		if (!cl)
			continue;

		dev_dbg(&dev->pdev->dev, "completing call back.\n");
		if (cl == &dev->iamthif_cl)
			mei_amthif_complete(dev, cb);
		else
			mei_cl_complete_handler(cl, cb);
	}
}
84
EXPORT_SYMBOL_GPL(mei_irq_compl_handler);
O
Oren Weil 已提交
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
/**
 * _mei_irq_thread_state_ok - checks if mei header matches file private data
 *
 * @cl: private data of the file object
 * @mei_hdr: header of mei client message
 *
 * returns !=0 if matches, 0 if no match.
 */
static int _mei_irq_thread_state_ok(struct mei_cl *cl,
				struct mei_msg_hdr *mei_hdr)
{
	return (cl->host_client_id == mei_hdr->host_addr &&
		cl->me_client_id == mei_hdr->me_addr &&
		cl->state == MEI_FILE_CONNECTED &&
		MEI_READ_COMPLETE != cl->reading_state);
}

/**
 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
 * handle the read mei client message data processing.
 *
 * @complete_list: An instance of our list structure
 * @dev: the device structure
 * @mei_hdr: header of mei client message
 *
 * returns 0 on success, <0 on failure.
 */
112
static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
O
Oren Weil 已提交
113 114 115 116 117
		struct mei_device *dev,
		struct mei_msg_hdr *mei_hdr)
{
	struct mei_cl *cl;
	struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
118
	unsigned char *buffer = NULL;
O
Oren Weil 已提交
119 120

	dev_dbg(&dev->pdev->dev, "start client msg\n");
121
	if (list_empty(&dev->read_list.list))
O
Oren Weil 已提交
122 123
		goto quit;

124
	list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
125
		cl = cb_pos->cl;
O
Oren Weil 已提交
126 127
		if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
			cl->reading_state = MEI_READING;
128
			buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
O
Oren Weil 已提交
129 130

			if (cb_pos->response_buffer.size <
131
					mei_hdr->length + cb_pos->buf_idx) {
O
Oren Weil 已提交
132
				dev_dbg(&dev->pdev->dev, "message overflow.\n");
133
				list_del(&cb_pos->list);
O
Oren Weil 已提交
134 135 136 137 138
				return -ENOMEM;
			}
			if (buffer)
				mei_read_slots(dev, buffer, mei_hdr->length);

139
			cb_pos->buf_idx += mei_hdr->length;
O
Oren Weil 已提交
140 141
			if (mei_hdr->msg_complete) {
				cl->status = 0;
142
				list_del(&cb_pos->list);
O
Oren Weil 已提交
143
				dev_dbg(&dev->pdev->dev,
144
					"completed read H cl = %d, ME cl = %d, length = %lu\n",
O
Oren Weil 已提交
145 146
					cl->host_client_id,
					cl->me_client_id,
147 148
					cb_pos->buf_idx);

149 150
				list_add_tail(&cb_pos->list,
						&complete_list->list);
O
Oren Weil 已提交
151 152 153 154 155 156 157 158 159 160
			}

			break;
		}

	}

quit:
	dev_dbg(&dev->pdev->dev, "message read\n");
	if (!buffer) {
161
		mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
162 163
		dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n",
				MEI_HDR_PRM(mei_hdr));
O
Oren Weil 已提交
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	}

	return 0;
}

/**
 * _mei_irq_thread_close - processes close related operation.
 *
 * @dev: the device structure.
 * @slots: free slots.
 * @cb_pos: callback block.
 * @cl: private data of the file object.
 * @cmpl_list: complete list.
 *
 * returns 0, OK; otherwise, error.
 */
static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
				struct mei_cl_cb *cb_pos,
				struct mei_cl *cl,
183
				struct mei_cl_cb *cmpl_list)
O
Oren Weil 已提交
184
{
T
Tomas Winkler 已提交
185 186
	u32 msg_slots =
		mei_data2slots(sizeof(struct hbm_client_connect_request));
O
Oren Weil 已提交
187

T
Tomas Winkler 已提交
188 189 190 191
	if (*slots < msg_slots)
		return -EMSGSIZE;

	*slots -= msg_slots;
192

193
	if (mei_hbm_cl_disconnect_req(dev, cl)) {
194
		cl->status = 0;
195
		cb_pos->buf_idx = 0;
196
		list_move_tail(&cb_pos->list, &cmpl_list->list);
T
Tomas Winkler 已提交
197
		return -EIO;
O
Oren Weil 已提交
198 199
	}

T
Tomas Winkler 已提交
200 201 202 203 204 205
	cl->state = MEI_FILE_DISCONNECTING;
	cl->status = 0;
	cb_pos->buf_idx = 0;
	list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
	cl->timer_count = MEI_CONNECT_TIMEOUT;

O
Oren Weil 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	return 0;
}


/**
 * _mei_hb_read - processes read related operation.
 *
 * @dev: the device structure.
 * @slots: free slots.
 * @cb_pos: callback block.
 * @cl: private data of the file object.
 * @cmpl_list: complete list.
 *
 * returns 0, OK; otherwise, error.
 */
static int _mei_irq_thread_read(struct mei_device *dev,	s32 *slots,
			struct mei_cl_cb *cb_pos,
			struct mei_cl *cl,
224
			struct mei_cl_cb *cmpl_list)
O
Oren Weil 已提交
225
{
T
Tomas Winkler 已提交
226 227 228
	u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));

	if (*slots < msg_slots) {
O
Oren Weil 已提交
229
		/* return the cancel routine */
230
		list_del(&cb_pos->list);
T
Tomas Winkler 已提交
231
		return -EMSGSIZE;
O
Oren Weil 已提交
232 233
	}

T
Tomas Winkler 已提交
234
	*slots -= msg_slots;
235

236
	if (mei_hbm_cl_flow_control_req(dev, cl)) {
237
		cl->status = -ENODEV;
238
		cb_pos->buf_idx = 0;
239
		list_move_tail(&cb_pos->list, &cmpl_list->list);
240 241
		return -ENODEV;
	}
242
	list_move_tail(&cb_pos->list, &dev->read_list.list);
243

O
Oren Weil 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
	return 0;
}


/**
 * _mei_irq_thread_ioctl - processes ioctl related operation.
 *
 * @dev: the device structure.
 * @slots: free slots.
 * @cb_pos: callback block.
 * @cl: private data of the file object.
 * @cmpl_list: complete list.
 *
 * returns 0, OK; otherwise, error.
 */
static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
			struct mei_cl_cb *cb_pos,
			struct mei_cl *cl,
262
			struct mei_cl_cb *cmpl_list)
O
Oren Weil 已提交
263
{
T
Tomas Winkler 已提交
264 265 266 267
	u32 msg_slots =
		mei_data2slots(sizeof(struct hbm_client_connect_request));

	if (*slots < msg_slots) {
O
Oren Weil 已提交
268
		/* return the cancel routine */
269
		list_del(&cb_pos->list);
T
Tomas Winkler 已提交
270
		return -EMSGSIZE;
O
Oren Weil 已提交
271 272
	}

T
Tomas Winkler 已提交
273 274
	*slots -=  msg_slots;

275
	cl->state = MEI_FILE_CONNECTING;
T
Tomas Winkler 已提交
276

277
	if (mei_hbm_cl_connect_req(dev, cl)) {
278
		cl->status = -ENODEV;
279
		cb_pos->buf_idx = 0;
280
		list_del(&cb_pos->list);
281 282
		return -ENODEV;
	} else {
283
		list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
284 285
		cl->timer_count = MEI_CONNECT_TIMEOUT;
	}
O
Oren Weil 已提交
286 287 288 289
	return 0;
}

/**
290
 * mei_irq_thread_write_complete - write messages to device.
O
Oren Weil 已提交
291 292 293
 *
 * @dev: the device structure.
 * @slots: free slots.
294
 * @cb: callback block.
O
Oren Weil 已提交
295 296 297 298
 * @cmpl_list: complete list.
 *
 * returns 0, OK; otherwise, error.
 */
299 300
static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
			struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
O
Oren Weil 已提交
301
{
302
	struct mei_msg_hdr mei_hdr;
303 304
	struct mei_cl *cl = cb->cl;
	size_t len = cb->request_buffer.size - cb->buf_idx;
T
Tomas Winkler 已提交
305
	u32 msg_slots = mei_data2slots(len);
306

307 308 309
	mei_hdr.host_addr = cl->host_client_id;
	mei_hdr.me_addr = cl->me_client_id;
	mei_hdr.reserved = 0;
O
Oren Weil 已提交
310

311
	if (*slots >= msg_slots) {
312 313
		mei_hdr.length = len;
		mei_hdr.msg_complete = 1;
314
	/* Split the message only if we can write the whole host buffer */
315
	} else if (*slots == dev->hbuf_depth) {
316 317
		msg_slots = *slots;
		len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
318 319
		mei_hdr.length = len;
		mei_hdr.msg_complete = 0;
O
Oren Weil 已提交
320
	} else {
321 322
		/* wait for next time the host buffer is empty */
		return 0;
O
Oren Weil 已提交
323 324
	}

325 326
	dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
			cb->request_buffer.size, cb->buf_idx);
327
	dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
328 329

	*slots -=  msg_slots;
330
	if (mei_write_message(dev, &mei_hdr,
331
			cb->request_buffer.data + cb->buf_idx)) {
332 333 334 335 336
		cl->status = -ENODEV;
		list_move_tail(&cb->list, &cmpl_list->list);
		return -ENODEV;
	}

T
Tomas Winkler 已提交
337
	if (mei_cl_flow_ctrl_reduce(cl))
338 339 340
		return -ENODEV;

	cl->status = 0;
341 342
	cb->buf_idx += mei_hdr.length;
	if (mei_hdr.msg_complete)
343 344
		list_move_tail(&cb->list, &dev->write_waiting_list.list);

O
Oren Weil 已提交
345 346 347 348 349 350 351 352
	return 0;
}

/**
 * mei_irq_thread_read_handler - bottom half read routine after ISR to
 * handle the read processing.
 *
 * @dev: the device structure
353
 * @cmpl_list: An instance of our list structure
O
Oren Weil 已提交
354 355 356 357
 * @slots: slots to read.
 *
 * returns 0 on success, <0 on failure.
 */
358 359
int mei_irq_read_handler(struct mei_device *dev,
		struct mei_cl_cb *cmpl_list, s32 *slots)
O
Oren Weil 已提交
360 361 362 363 364 365 366
{
	struct mei_msg_hdr *mei_hdr;
	struct mei_cl *cl_pos = NULL;
	struct mei_cl *cl_next = NULL;
	int ret = 0;

	if (!dev->rd_msg_hdr) {
367
		dev->rd_msg_hdr = mei_read_hdr(dev);
O
Oren Weil 已提交
368 369 370 371 372
		dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
		(*slots)--;
		dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
	}
	mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
373
	dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
O
Oren Weil 已提交
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411

	if (mei_hdr->reserved || !dev->rd_msg_hdr) {
		dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
		ret = -EBADMSG;
		goto end;
	}

	if (mei_hdr->host_addr || mei_hdr->me_addr) {
		list_for_each_entry_safe(cl_pos, cl_next,
					&dev->file_list, link) {
			dev_dbg(&dev->pdev->dev,
					"list_for_each_entry_safe read host"
					" client = %d, ME client = %d\n",
					cl_pos->host_client_id,
					cl_pos->me_client_id);
			if (cl_pos->host_client_id == mei_hdr->host_addr &&
			    cl_pos->me_client_id == mei_hdr->me_addr)
				break;
		}

		if (&cl_pos->link == &dev->file_list) {
			dev_dbg(&dev->pdev->dev, "corrupted message header\n");
			ret = -EBADMSG;
			goto end;
		}
	}
	if (((*slots) * sizeof(u32)) < mei_hdr->length) {
		dev_dbg(&dev->pdev->dev,
				"we can't read the message slots =%08x.\n",
				*slots);
		/* we can't read the message */
		ret = -ERANGE;
		goto end;
	}

	/* decide where to read the message too */
	if (!mei_hdr->host_addr) {
		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
412
		mei_hbm_dispatch(dev, mei_hdr);
O
Oren Weil 已提交
413 414 415 416 417
		dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
	} else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
		   (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
		   (dev->iamthif_state == MEI_IAMTHIF_READING)) {
		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
418 419

		dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
420 421

		ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
O
Oren Weil 已提交
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
		if (ret)
			goto end;
	} else {
		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
		ret = mei_irq_thread_read_client_message(cmpl_list,
							 dev, mei_hdr);
		if (ret)
			goto end;

	}

	/* reset the number of slots and header */
	*slots = mei_count_full_read_slots(dev);
	dev->rd_msg_hdr = 0;

	if (*slots == -EOVERFLOW) {
		/* overflow - reset */
		dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
		/* set the event since message has been read */
		ret = -ERANGE;
		goto end;
	}
end:
	return ret;
}
447
EXPORT_SYMBOL_GPL(mei_irq_read_handler);
O
Oren Weil 已提交
448 449 450


/**
451 452
 * mei_irq_write_handler -  dispatch write requests
 *  after irq received
O
Oren Weil 已提交
453 454
 *
 * @dev: the device structure
455
 * @cmpl_list: An instance of our list structure
O
Oren Weil 已提交
456 457 458
 *
 * returns 0 on success, <0 on failure.
 */
T
Tomas Winkler 已提交
459
int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
O
Oren Weil 已提交
460 461 462
{

	struct mei_cl *cl;
463
	struct mei_cl_cb *pos = NULL, *next = NULL;
464
	struct mei_cl_cb *list;
465
	s32 slots;
O
Oren Weil 已提交
466 467
	int ret;

468
	if (!mei_hbuf_is_ready(dev)) {
O
Oren Weil 已提交
469 470 471
		dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
		return 0;
	}
472 473
	slots = mei_hbuf_empty_slots(dev);
	if (slots <= 0)
474 475
		return -EMSGSIZE;

O
Oren Weil 已提交
476 477 478 479
	/* complete all waiting for write CB */
	dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");

	list = &dev->write_waiting_list;
480
	list_for_each_entry_safe(pos, next, &list->list, list) {
481
		cl = pos->cl;
482 483 484 485
		if (cl == NULL)
			continue;

		cl->status = 0;
486
		list_del(&pos->list);
487
		if (MEI_WRITING == cl->writing_state &&
488 489
		    pos->fop_type == MEI_FOP_WRITE &&
		    cl != &dev->iamthif_cl) {
490
			dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
491
			cl->writing_state = MEI_WRITE_COMPLETE;
492
			list_add_tail(&pos->list, &cmpl_list->list);
493 494 495 496
		}
		if (cl == &dev->iamthif_cl) {
			dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
			if (dev->iamthif_flow_control_pending) {
497
				ret = mei_amthif_irq_read(dev, &slots);
498 499
				if (ret)
					return ret;
O
Oren Weil 已提交
500 501 502 503
			}
		}
	}

504 505
	if (dev->wd_state == MEI_WD_STOPPING) {
		dev->wd_state = MEI_WD_IDLE;
O
Oren Weil 已提交
506 507 508
		wake_up_interruptible(&dev->wait_stop_wd);
	}

509 510
	if (dev->wr_ext_msg.hdr.length) {
		mei_write_message(dev, &dev->wr_ext_msg.hdr,
511
				dev->wr_ext_msg.data);
512
		slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
513
		dev->wr_ext_msg.hdr.length = 0;
O
Oren Weil 已提交
514
	}
515
	if (dev->dev_state == MEI_DEV_ENABLED) {
O
Oren Weil 已提交
516
		if (dev->wd_pending &&
T
Tomas Winkler 已提交
517
		    mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) {
O
Oren Weil 已提交
518 519
			if (mei_wd_send(dev))
				dev_dbg(&dev->pdev->dev, "wd send failed.\n");
T
Tomas Winkler 已提交
520
			else if (mei_cl_flow_ctrl_reduce(&dev->wd_cl))
521
				return -ENODEV;
O
Oren Weil 已提交
522

523
			dev->wd_pending = false;
O
Oren Weil 已提交
524

525
			if (dev->wd_state == MEI_WD_RUNNING)
526
				slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
527
			else
528
				slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
O
Oren Weil 已提交
529 530 531 532
		}
	}

	/* complete control write list CB */
533
	dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
534
	list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
535
		cl = pos->cl;
536
		if (!cl) {
537
			list_del(&pos->list);
538 539
			return -ENODEV;
		}
540 541
		switch (pos->fop_type) {
		case MEI_FOP_CLOSE:
542
			/* send disconnect message */
543 544
			ret = _mei_irq_thread_close(dev, &slots, pos,
						cl, cmpl_list);
545 546
			if (ret)
				return ret;
O
Oren Weil 已提交
547

548
			break;
549
		case MEI_FOP_READ:
550
			/* send flow control message */
551 552
			ret = _mei_irq_thread_read(dev, &slots, pos,
						cl, cmpl_list);
553 554
			if (ret)
				return ret;
O
Oren Weil 已提交
555

556
			break;
557
		case MEI_FOP_IOCTL:
558
			/* connect message */
T
Tomas Winkler 已提交
559
			if (mei_cl_is_other_connecting(cl))
560
				continue;
561 562
			ret = _mei_irq_thread_ioctl(dev, &slots, pos,
						cl, cmpl_list);
563 564
			if (ret)
				return ret;
O
Oren Weil 已提交
565

566
			break;
O
Oren Weil 已提交
567

568 569
		default:
			BUG();
O
Oren Weil 已提交
570
		}
571

O
Oren Weil 已提交
572 573
	}
	/* complete  write list CB */
574
	dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
575
	list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
576
		cl = pos->cl;
577 578
		if (cl == NULL)
			continue;
T
Tomas Winkler 已提交
579
		if (mei_cl_flow_ctrl_creds(cl) <= 0) {
580 581 582 583 584
			dev_dbg(&dev->pdev->dev,
				"No flow control credentials for client %d, not sending.\n",
				cl->host_client_id);
			continue;
		}
585

586
		if (cl == &dev->iamthif_cl)
587
			ret = mei_amthif_irq_write_complete(dev, &slots,
588
							pos, cmpl_list);
589 590 591 592 593
		else
			ret = mei_irq_thread_write_complete(dev, &slots, pos,
						cmpl_list);
		if (ret)
			return ret;
594

O
Oren Weil 已提交
595 596 597
	}
	return 0;
}
598
EXPORT_SYMBOL_GPL(mei_irq_write_handler);
O
Oren Weil 已提交
599 600 601 602 603 604 605 606 607 608



/**
 * mei_timer - timer function.
 *
 * @work: pointer to the work_struct structure
 *
 * NOTE: This function is called by timer interrupt work
 */
609
void mei_timer(struct work_struct *work)
O
Oren Weil 已提交
610 611 612 613 614 615 616 617
{
	unsigned long timeout;
	struct mei_cl *cl_pos = NULL;
	struct mei_cl *cl_next = NULL;
	struct mei_cl_cb  *cb_pos = NULL;
	struct mei_cl_cb  *cb_next = NULL;

	struct mei_device *dev = container_of(work,
618
					struct mei_device, timer_work.work);
O
Oren Weil 已提交
619 620 621


	mutex_lock(&dev->device_lock);
622 623
	if (dev->dev_state != MEI_DEV_ENABLED) {
		if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
O
Oren Weil 已提交
624 625
			if (dev->init_clients_timer) {
				if (--dev->init_clients_timer == 0) {
T
Tomas Winkler 已提交
626 627
					dev_err(&dev->pdev->dev, "reset: init clients timeout hbm_state = %d.\n",
						dev->hbm_state);
O
Oren Weil 已提交
628 629 630 631 632 633 634 635 636 637
					mei_reset(dev, 1);
				}
			}
		}
		goto out;
	}
	/*** connect/disconnect timeouts ***/
	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
		if (cl_pos->timer_count) {
			if (--cl_pos->timer_count == 0) {
638
				dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n");
O
Oren Weil 已提交
639 640 641 642 643 644 645 646
				mei_reset(dev, 1);
				goto out;
			}
		}
	}

	if (dev->iamthif_stall_timer) {
		if (--dev->iamthif_stall_timer == 0) {
647
			dev_err(&dev->pdev->dev, "reset: amthif  hanged.\n");
O
Oren Weil 已提交
648 649 650
			mei_reset(dev, 1);
			dev->iamthif_msg_buf_size = 0;
			dev->iamthif_msg_buf_index = 0;
651 652
			dev->iamthif_canceled = false;
			dev->iamthif_ioctl = true;
O
Oren Weil 已提交
653 654 655
			dev->iamthif_state = MEI_IAMTHIF_IDLE;
			dev->iamthif_timer = 0;

656 657
			mei_io_cb_free(dev->iamthif_current_cb);
			dev->iamthif_current_cb = NULL;
O
Oren Weil 已提交
658 659

			dev->iamthif_file_object = NULL;
660
			mei_amthif_run_next_cmd(dev);
O
Oren Weil 已提交
661 662 663 664 665 666
		}
	}

	if (dev->iamthif_timer) {

		timeout = dev->iamthif_timer +
667
			mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
O
Oren Weil 已提交
668 669 670 671 672 673 674 675 676 677 678 679 680

		dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
				dev->iamthif_timer);
		dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
		dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
		if (time_after(jiffies, timeout)) {
			/*
			 * User didn't read the AMTHI data on time (15sec)
			 * freeing AMTHI for other requests
			 */

			dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");

681 682
			list_for_each_entry_safe(cb_pos, cb_next,
				&dev->amthif_rd_complete_list.list, list) {
O
Oren Weil 已提交
683

684
				cl_pos = cb_pos->file_object->private_data;
O
Oren Weil 已提交
685

686 687
				/* Finding the AMTHI entry. */
				if (cl_pos == &dev->iamthif_cl)
688
					list_del(&cb_pos->list);
O
Oren Weil 已提交
689
			}
690 691
			mei_io_cb_free(dev->iamthif_current_cb);
			dev->iamthif_current_cb = NULL;
O
Oren Weil 已提交
692 693 694 695

			dev->iamthif_file_object->private_data = NULL;
			dev->iamthif_file_object = NULL;
			dev->iamthif_timer = 0;
696
			mei_amthif_run_next_cmd(dev);
O
Oren Weil 已提交
697 698 699 700

		}
	}
out:
701 702
	schedule_delayed_work(&dev->timer_work, 2 * HZ);
	mutex_unlock(&dev->device_lock);
O
Oren Weil 已提交
703 704
}