blob: eb744cc4f72a5dbeaccb5a08ecc0751a480f9982 [file] [log] [blame]
Oren Weilfb7d8792011-05-15 13:43:42 +03001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
Tomas Winkler733ba912012-02-09 19:25:53 +02004 * Copyright (c) 2003-2012, Intel Corporation.
Oren Weilfb7d8792011-05-15 13:43:42 +03005 *
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
18#include <linux/pci.h>
19#include <linux/kthread.h>
20#include <linux/interrupt.h>
21#include <linux/fs.h>
22#include <linux/jiffies.h>
23
Tomas Winkler4f3afe12012-05-09 16:38:59 +030024#include <linux/mei.h>
Tomas Winkler47a73802012-12-25 19:06:03 +020025
26#include "mei_dev.h"
Oren Weilfb7d8792011-05-15 13:43:42 +030027#include "interface.h"
28
29
30/**
Oren Weilfb7d8792011-05-15 13:43:42 +030031 * _mei_cmpl - processes completed operation.
32 *
33 * @cl: private data of the file object.
34 * @cb_pos: callback block.
35 */
36static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
37{
Tomas Winkler4b8960b2012-11-11 17:38:00 +020038 if (cb_pos->fop_type == MEI_FOP_WRITE) {
Tomas Winkler601a1ef2012-10-09 16:50:20 +020039 mei_io_cb_free(cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +030040 cb_pos = NULL;
41 cl->writing_state = MEI_WRITE_COMPLETE;
42 if (waitqueue_active(&cl->tx_wait))
43 wake_up_interruptible(&cl->tx_wait);
44
Tomas Winkler4b8960b2012-11-11 17:38:00 +020045 } else if (cb_pos->fop_type == MEI_FOP_READ &&
Oren Weilfb7d8792011-05-15 13:43:42 +030046 MEI_READING == cl->reading_state) {
47 cl->reading_state = MEI_READ_COMPLETE;
48 if (waitqueue_active(&cl->rx_wait))
49 wake_up_interruptible(&cl->rx_wait);
50
51 }
52}
53
54/**
Oren Weilfb7d8792011-05-15 13:43:42 +030055 * _mei_irq_thread_state_ok - checks if mei header matches file private data
56 *
57 * @cl: private data of the file object
58 * @mei_hdr: header of mei client message
59 *
60 * returns !=0 if matches, 0 if no match.
61 */
62static int _mei_irq_thread_state_ok(struct mei_cl *cl,
63 struct mei_msg_hdr *mei_hdr)
64{
65 return (cl->host_client_id == mei_hdr->host_addr &&
66 cl->me_client_id == mei_hdr->me_addr &&
67 cl->state == MEI_FILE_CONNECTED &&
68 MEI_READ_COMPLETE != cl->reading_state);
69}
70
71/**
72 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
73 * handle the read mei client message data processing.
74 *
75 * @complete_list: An instance of our list structure
76 * @dev: the device structure
77 * @mei_hdr: header of mei client message
78 *
79 * returns 0 on success, <0 on failure.
80 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +020081static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
Oren Weilfb7d8792011-05-15 13:43:42 +030082 struct mei_device *dev,
83 struct mei_msg_hdr *mei_hdr)
84{
85 struct mei_cl *cl;
86 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
Tomas Winkler479bc592011-06-16 00:46:03 +030087 unsigned char *buffer = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +030088
89 dev_dbg(&dev->pdev->dev, "start client msg\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +020090 if (list_empty(&dev->read_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +030091 goto quit;
92
Tomas Winklerfb601ad2012-10-15 12:06:48 +020093 list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +020094 cl = cb_pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +030095 if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
96 cl->reading_state = MEI_READING;
Tomas Winklerebb108ef2012-10-09 16:50:16 +020097 buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +030098
99 if (cb_pos->response_buffer.size <
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200100 mei_hdr->length + cb_pos->buf_idx) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300101 dev_dbg(&dev->pdev->dev, "message overflow.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200102 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300103 return -ENOMEM;
104 }
105 if (buffer)
106 mei_read_slots(dev, buffer, mei_hdr->length);
107
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200108 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300109 if (mei_hdr->msg_complete) {
110 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200111 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300112 dev_dbg(&dev->pdev->dev,
Tomas Winklera4136b42012-09-11 00:43:22 +0300113 "completed read H cl = %d, ME cl = %d, length = %lu\n",
Oren Weilfb7d8792011-05-15 13:43:42 +0300114 cl->host_client_id,
115 cl->me_client_id,
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200116 cb_pos->buf_idx);
117
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200118 list_add_tail(&cb_pos->list,
119 &complete_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300120 }
121
122 break;
123 }
124
125 }
126
127quit:
128 dev_dbg(&dev->pdev->dev, "message read\n");
129 if (!buffer) {
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200130 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200131 dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n",
132 MEI_HDR_PRM(mei_hdr));
Oren Weilfb7d8792011-05-15 13:43:42 +0300133 }
134
135 return 0;
136}
137
138/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300139 * _mei_irq_thread_close - processes close related operation.
140 *
141 * @dev: the device structure.
142 * @slots: free slots.
143 * @cb_pos: callback block.
144 * @cl: private data of the file object.
145 * @cmpl_list: complete list.
146 *
147 * returns 0, OK; otherwise, error.
148 */
149static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
150 struct mei_cl_cb *cb_pos,
151 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200152 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300153{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300154 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200155 sizeof(struct hbm_client_connect_request)))
Oren Weilfb7d8792011-05-15 13:43:42 +0300156 return -EBADMSG;
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300157
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200158 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300159
Tomas Winkler8120e722012-12-25 19:06:11 +0200160 if (mei_hbm_cl_disconnect_req(dev, cl)) {
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300161 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200162 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200163 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300164 return -EMSGSIZE;
165 } else {
166 cl->state = MEI_FILE_DISCONNECTING;
167 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200168 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200169 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300170 cl->timer_count = MEI_CONNECT_TIMEOUT;
Oren Weilfb7d8792011-05-15 13:43:42 +0300171 }
172
173 return 0;
174}
175
176/**
177 * is_treat_specially_client - checks if the message belongs
178 * to the file private data.
179 *
180 * @cl: private data of the file object
181 * @rs: connect response bus message
182 *
183 */
184static bool is_treat_specially_client(struct mei_cl *cl,
185 struct hbm_client_connect_response *rs)
186{
187
188 if (cl->host_client_id == rs->host_addr &&
189 cl->me_client_id == rs->me_addr) {
190 if (!rs->status) {
191 cl->state = MEI_FILE_CONNECTED;
192 cl->status = 0;
193
194 } else {
195 cl->state = MEI_FILE_DISCONNECTED;
196 cl->status = -ENODEV;
197 }
198 cl->timer_count = 0;
199
200 return true;
201 }
202 return false;
203}
204
205/**
206 * mei_client_connect_response - connects to response irq routine
207 *
208 * @dev: the device structure
209 * @rs: connect response bus message
210 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200211void mei_client_connect_response(struct mei_device *dev,
Oren Weilfb7d8792011-05-15 13:43:42 +0300212 struct hbm_client_connect_response *rs)
213{
214
215 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200216 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300217
218 dev_dbg(&dev->pdev->dev,
219 "connect_response:\n"
220 "ME Client = %d\n"
221 "Host Client = %d\n"
222 "Status = %d\n",
223 rs->me_addr,
224 rs->host_addr,
225 rs->status);
226
227 /* if WD or iamthif client treat specially */
228
229 if (is_treat_specially_client(&(dev->wd_cl), rs)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300230 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
Tomas Winkler70cd5332011-12-22 18:50:50 +0200231 mei_watchdog_register(dev);
Oren Weil9ce178e2011-09-07 09:03:09 +0300232
Oren Weilfb7d8792011-05-15 13:43:42 +0300233 return;
234 }
235
236 if (is_treat_specially_client(&(dev->iamthif_cl), rs)) {
237 dev->iamthif_state = MEI_IAMTHIF_IDLE;
238 return;
239 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200240 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200241
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200242 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200243 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200244 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200245 return;
246 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200247 if (pos->fop_type == MEI_FOP_IOCTL) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200248 if (is_treat_specially_client(cl, rs)) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200249 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200250 cl->status = 0;
251 cl->timer_count = 0;
252 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300253 }
254 }
255 }
256}
257
258/**
259 * mei_client_disconnect_response - disconnects from response irq routine
260 *
261 * @dev: the device structure
262 * @rs: disconnect response bus message
263 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200264void mei_client_disconnect_response(struct mei_device *dev,
265 struct hbm_client_connect_response *rs)
Oren Weilfb7d8792011-05-15 13:43:42 +0300266{
267 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200268 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300269
270 dev_dbg(&dev->pdev->dev,
271 "disconnect_response:\n"
272 "ME Client = %d\n"
273 "Host Client = %d\n"
274 "Status = %d\n",
275 rs->me_addr,
276 rs->host_addr,
277 rs->status);
278
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200279 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200280 cl = pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300281
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200282 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200283 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200284 return;
285 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300286
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200287 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
288 if (cl->host_client_id == rs->host_addr &&
289 cl->me_client_id == rs->me_addr) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300290
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200291 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200292 if (!rs->status)
293 cl->state = MEI_FILE_DISCONNECTED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300294
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200295 cl->status = 0;
296 cl->timer_count = 0;
297 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300298 }
299 }
300}
301
302/**
303 * same_flow_addr - tells if they have the same address.
304 *
305 * @file: private data of the file object.
306 * @flow: flow control.
307 *
308 * returns !=0, same; 0,not.
309 */
310static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow)
311{
312 return (cl->host_client_id == flow->host_addr &&
313 cl->me_client_id == flow->me_addr);
314}
315
316/**
317 * add_single_flow_creds - adds single buffer credentials.
318 *
319 * @file: private data ot the file object.
320 * @flow: flow control.
321 */
322static void add_single_flow_creds(struct mei_device *dev,
323 struct hbm_flow_control *flow)
324{
325 struct mei_me_client *client;
326 int i;
327
Tomas Winklercf9673d2011-06-06 10:44:33 +0300328 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300329 client = &dev->me_clients[i];
330 if (client && flow->me_addr == client->client_id) {
331 if (client->props.single_recv_buf) {
332 client->mei_flow_ctrl_creds++;
333 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
334 flow->me_addr);
335 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
336 client->mei_flow_ctrl_creds);
337 } else {
338 BUG(); /* error in flow control */
339 }
340 }
341 }
342}
343
344/**
345 * mei_client_flow_control_response - flow control response irq routine
346 *
347 * @dev: the device structure
348 * @flow_control: flow control response bus message
349 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200350void mei_client_flow_control_response(struct mei_device *dev,
Oren Weilfb7d8792011-05-15 13:43:42 +0300351 struct hbm_flow_control *flow_control)
352{
353 struct mei_cl *cl_pos = NULL;
354 struct mei_cl *cl_next = NULL;
355
356 if (!flow_control->host_addr) {
357 /* single receive buffer */
358 add_single_flow_creds(dev, flow_control);
359 } else {
360 /* normal connection */
361 list_for_each_entry_safe(cl_pos, cl_next,
362 &dev->file_list, link) {
363 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n");
364
365 dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n",
366 cl_pos->host_client_id,
367 cl_pos->me_client_id);
368 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
369 flow_control->host_addr,
370 flow_control->me_addr);
371 if (same_flow_addr(cl_pos, flow_control)) {
372 dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n",
373 flow_control->host_addr,
374 flow_control->me_addr);
375 cl_pos->mei_flow_ctrl_creds++;
376 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
377 cl_pos->mei_flow_ctrl_creds);
378 break;
379 }
380 }
381 }
382}
383
Oren Weilfb7d8792011-05-15 13:43:42 +0300384
385/**
386 * _mei_hb_read - processes read related operation.
387 *
388 * @dev: the device structure.
389 * @slots: free slots.
390 * @cb_pos: callback block.
391 * @cl: private data of the file object.
392 * @cmpl_list: complete list.
393 *
394 * returns 0, OK; otherwise, error.
395 */
396static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
397 struct mei_cl_cb *cb_pos,
398 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200399 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300400{
Tomas Winkler1e69d642012-05-29 16:39:12 +0300401 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300402 sizeof(struct hbm_flow_control))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300403 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200404 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300405 return -EBADMSG;
406 }
407
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300408 *slots -= mei_data2slots(sizeof(struct hbm_flow_control));
409
Tomas Winkler8120e722012-12-25 19:06:11 +0200410 if (mei_hbm_cl_flow_control_req(dev, cl)) {
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200411 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200412 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200413 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200414 return -ENODEV;
415 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200416 list_move_tail(&cb_pos->list, &dev->read_list.list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200417
Oren Weilfb7d8792011-05-15 13:43:42 +0300418 return 0;
419}
420
421
422/**
423 * _mei_irq_thread_ioctl - processes ioctl related operation.
424 *
425 * @dev: the device structure.
426 * @slots: free slots.
427 * @cb_pos: callback block.
428 * @cl: private data of the file object.
429 * @cmpl_list: complete list.
430 *
431 * returns 0, OK; otherwise, error.
432 */
433static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
434 struct mei_cl_cb *cb_pos,
435 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200436 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300437{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300438 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300439 sizeof(struct hbm_client_connect_request))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300440 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200441 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300442 return -EBADMSG;
443 }
444
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300445 cl->state = MEI_FILE_CONNECTING;
Tomas Winkler8120e722012-12-25 19:06:11 +0200446 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
447 if (mei_hbm_cl_connect_req(dev, cl)) {
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300448 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200449 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200450 list_del(&cb_pos->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300451 return -ENODEV;
452 } else {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200453 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300454 cl->timer_count = MEI_CONNECT_TIMEOUT;
455 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300456 return 0;
457}
458
459/**
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200460 * mei_irq_thread_write_complete - write messages to device.
Oren Weilfb7d8792011-05-15 13:43:42 +0300461 *
462 * @dev: the device structure.
463 * @slots: free slots.
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200464 * @cb: callback block.
Oren Weilfb7d8792011-05-15 13:43:42 +0300465 * @cmpl_list: complete list.
466 *
467 * returns 0, OK; otherwise, error.
468 */
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200469static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
470 struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300471{
Tomas Winklere46f1872012-12-25 19:06:10 +0200472 struct mei_msg_hdr mei_hdr;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200473 struct mei_cl *cl = cb->cl;
474 size_t len = cb->request_buffer.size - cb->buf_idx;
475 size_t msg_slots = mei_data2slots(len);
Oren Weilfb7d8792011-05-15 13:43:42 +0300476
Tomas Winklere46f1872012-12-25 19:06:10 +0200477 mei_hdr.host_addr = cl->host_client_id;
478 mei_hdr.me_addr = cl->me_client_id;
479 mei_hdr.reserved = 0;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200480
481 if (*slots >= msg_slots) {
Tomas Winklere46f1872012-12-25 19:06:10 +0200482 mei_hdr.length = len;
483 mei_hdr.msg_complete = 1;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200484 /* Split the message only if we can write the whole host buffer */
Tomas Winkler24aadc82012-06-25 23:46:27 +0300485 } else if (*slots == dev->hbuf_depth) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200486 msg_slots = *slots;
487 len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
Tomas Winklere46f1872012-12-25 19:06:10 +0200488 mei_hdr.length = len;
489 mei_hdr.msg_complete = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300490 } else {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200491 /* wait for next time the host buffer is empty */
492 return 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300493 }
494
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200495 dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
496 cb->request_buffer.size, cb->buf_idx);
Tomas Winklere46f1872012-12-25 19:06:10 +0200497 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200498
499 *slots -= msg_slots;
Tomas Winklere46f1872012-12-25 19:06:10 +0200500 if (mei_write_message(dev, &mei_hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200501 cb->request_buffer.data + cb->buf_idx)) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200502 cl->status = -ENODEV;
503 list_move_tail(&cb->list, &cmpl_list->list);
504 return -ENODEV;
505 }
506
507 if (mei_flow_ctrl_reduce(dev, cl))
508 return -ENODEV;
509
510 cl->status = 0;
Tomas Winklere46f1872012-12-25 19:06:10 +0200511 cb->buf_idx += mei_hdr.length;
512 if (mei_hdr.msg_complete)
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200513 list_move_tail(&cb->list, &dev->write_waiting_list.list);
514
Oren Weilfb7d8792011-05-15 13:43:42 +0300515 return 0;
516}
517
518/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300519 * mei_irq_thread_read_handler - bottom half read routine after ISR to
520 * handle the read processing.
521 *
522 * @cmpl_list: An instance of our list structure
523 * @dev: the device structure
524 * @slots: slots to read.
525 *
526 * returns 0 on success, <0 on failure.
527 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200528static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300529 struct mei_device *dev,
530 s32 *slots)
531{
532 struct mei_msg_hdr *mei_hdr;
533 struct mei_cl *cl_pos = NULL;
534 struct mei_cl *cl_next = NULL;
535 int ret = 0;
536
537 if (!dev->rd_msg_hdr) {
538 dev->rd_msg_hdr = mei_mecbrw_read(dev);
539 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
540 (*slots)--;
541 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
542 }
543 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200544 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Oren Weilfb7d8792011-05-15 13:43:42 +0300545
546 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
547 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
548 ret = -EBADMSG;
549 goto end;
550 }
551
552 if (mei_hdr->host_addr || mei_hdr->me_addr) {
553 list_for_each_entry_safe(cl_pos, cl_next,
554 &dev->file_list, link) {
555 dev_dbg(&dev->pdev->dev,
556 "list_for_each_entry_safe read host"
557 " client = %d, ME client = %d\n",
558 cl_pos->host_client_id,
559 cl_pos->me_client_id);
560 if (cl_pos->host_client_id == mei_hdr->host_addr &&
561 cl_pos->me_client_id == mei_hdr->me_addr)
562 break;
563 }
564
565 if (&cl_pos->link == &dev->file_list) {
566 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
567 ret = -EBADMSG;
568 goto end;
569 }
570 }
571 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
572 dev_dbg(&dev->pdev->dev,
573 "we can't read the message slots =%08x.\n",
574 *slots);
575 /* we can't read the message */
576 ret = -ERANGE;
577 goto end;
578 }
579
580 /* decide where to read the message too */
581 if (!mei_hdr->host_addr) {
582 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200583 mei_hbm_dispatch(dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300584 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
585 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
586 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
587 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
588 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200589
590 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Tomas Winkler19838fb2012-11-01 21:17:15 +0200591
592 ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300593 if (ret)
594 goto end;
Oren Weilfb7d8792011-05-15 13:43:42 +0300595 } else {
596 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
597 ret = mei_irq_thread_read_client_message(cmpl_list,
598 dev, mei_hdr);
599 if (ret)
600 goto end;
601
602 }
603
604 /* reset the number of slots and header */
605 *slots = mei_count_full_read_slots(dev);
606 dev->rd_msg_hdr = 0;
607
608 if (*slots == -EOVERFLOW) {
609 /* overflow - reset */
610 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
611 /* set the event since message has been read */
612 ret = -ERANGE;
613 goto end;
614 }
615end:
616 return ret;
617}
618
619
620/**
621 * mei_irq_thread_write_handler - bottom half write routine after
622 * ISR to handle the write processing.
623 *
Oren Weilfb7d8792011-05-15 13:43:42 +0300624 * @dev: the device structure
Tomas Winkler9a84d612012-11-18 15:13:18 +0200625 * @cmpl_list: An instance of our list structure
Oren Weilfb7d8792011-05-15 13:43:42 +0300626 *
627 * returns 0 on success, <0 on failure.
628 */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200629static int mei_irq_thread_write_handler(struct mei_device *dev,
630 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300631{
632
633 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200634 struct mei_cl_cb *pos = NULL, *next = NULL;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200635 struct mei_cl_cb *list;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200636 s32 slots;
Oren Weilfb7d8792011-05-15 13:43:42 +0300637 int ret;
638
Tomas Winkler726917f2012-06-25 23:46:28 +0300639 if (!mei_hbuf_is_empty(dev)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300640 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
641 return 0;
642 }
Tomas Winkler9a84d612012-11-18 15:13:18 +0200643 slots = mei_hbuf_empty_slots(dev);
644 if (slots <= 0)
Tomas Winkler7d5e0e52012-06-19 09:13:36 +0300645 return -EMSGSIZE;
646
Oren Weilfb7d8792011-05-15 13:43:42 +0300647 /* complete all waiting for write CB */
648 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
649
650 list = &dev->write_waiting_list;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200651 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200652 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200653 if (cl == NULL)
654 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +0300655
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200656 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200657 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200658 if (MEI_WRITING == cl->writing_state &&
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200659 pos->fop_type == MEI_FOP_WRITE &&
660 cl != &dev->iamthif_cl) {
Tomas Winkler483136e2012-07-04 19:24:54 +0300661 dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200662 cl->writing_state = MEI_WRITE_COMPLETE;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200663 list_add_tail(&pos->list, &cmpl_list->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200664 }
665 if (cl == &dev->iamthif_cl) {
666 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
667 if (dev->iamthif_flow_control_pending) {
Tomas Winkler9a84d612012-11-18 15:13:18 +0200668 ret = mei_amthif_irq_read(dev, &slots);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200669 if (ret)
670 return ret;
671 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300672 }
673 }
674
Tomas Winklerc216fde2012-08-16 19:39:43 +0300675 if (dev->wd_state == MEI_WD_STOPPING) {
676 dev->wd_state = MEI_WD_IDLE;
Oren Weilfb7d8792011-05-15 13:43:42 +0300677 wake_up_interruptible(&dev->wait_stop_wd);
Oren Weilfb7d8792011-05-15 13:43:42 +0300678 }
679
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200680 if (dev->wr_ext_msg.hdr.length) {
681 mei_write_message(dev, &dev->wr_ext_msg.hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200682 dev->wr_ext_msg.data);
Tomas Winkler9a84d612012-11-18 15:13:18 +0200683 slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200684 dev->wr_ext_msg.hdr.length = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300685 }
Tomas Winklerb210d752012-08-07 00:03:56 +0300686 if (dev->dev_state == MEI_DEV_ENABLED) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300687 if (dev->wd_pending &&
Tomas Winkler483136e2012-07-04 19:24:54 +0300688 mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300689 if (mei_wd_send(dev))
690 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
Tomas Winkler483136e2012-07-04 19:24:54 +0300691 else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl))
692 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300693
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300694 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300695
Tomas Winklerc216fde2012-08-16 19:39:43 +0300696 if (dev->wd_state == MEI_WD_RUNNING)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200697 slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300698 else
Tomas Winkler9a84d612012-11-18 15:13:18 +0200699 slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
Oren Weilfb7d8792011-05-15 13:43:42 +0300700 }
701 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300702
703 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +0200704 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200705 list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200706 cl = pos->cl;
Tomas Winklerc8372092011-11-27 21:43:33 +0200707 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200708 list_del(&pos->list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200709 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300710 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200711 switch (pos->fop_type) {
712 case MEI_FOP_CLOSE:
Tomas Winklerc8372092011-11-27 21:43:33 +0200713 /* send disconnect message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200714 ret = _mei_irq_thread_close(dev, &slots, pos,
715 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200716 if (ret)
717 return ret;
718
719 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200720 case MEI_FOP_READ:
Tomas Winklerc8372092011-11-27 21:43:33 +0200721 /* send flow control message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200722 ret = _mei_irq_thread_read(dev, &slots, pos,
723 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200724 if (ret)
725 return ret;
726
727 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200728 case MEI_FOP_IOCTL:
Tomas Winklerc8372092011-11-27 21:43:33 +0200729 /* connect message */
Natalia Ovsyanikove8cd29d2011-12-05 00:16:54 +0200730 if (mei_other_client_is_connecting(dev, cl))
Tomas Winklerc8372092011-11-27 21:43:33 +0200731 continue;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200732 ret = _mei_irq_thread_ioctl(dev, &slots, pos,
733 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200734 if (ret)
735 return ret;
736
737 break;
738
739 default:
740 BUG();
741 }
742
Oren Weilfb7d8792011-05-15 13:43:42 +0300743 }
744 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200745 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200746 list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200747 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200748 if (cl == NULL)
749 continue;
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200750 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
751 dev_dbg(&dev->pdev->dev,
752 "No flow control credentials for client %d, not sending.\n",
753 cl->host_client_id);
754 continue;
755 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300756
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200757 if (cl == &dev->iamthif_cl)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200758 ret = mei_amthif_irq_write_complete(dev, &slots,
Tomas Winkler24c656e2012-11-18 15:13:17 +0200759 pos, cmpl_list);
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200760 else
761 ret = mei_irq_thread_write_complete(dev, &slots, pos,
762 cmpl_list);
763 if (ret)
764 return ret;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200765
Oren Weilfb7d8792011-05-15 13:43:42 +0300766 }
767 return 0;
768}
769
770
771
772/**
773 * mei_timer - timer function.
774 *
775 * @work: pointer to the work_struct structure
776 *
777 * NOTE: This function is called by timer interrupt work
778 */
Oren Weila61c6532011-09-07 09:03:13 +0300779void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +0300780{
781 unsigned long timeout;
782 struct mei_cl *cl_pos = NULL;
783 struct mei_cl *cl_next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300784 struct mei_cl_cb *cb_pos = NULL;
785 struct mei_cl_cb *cb_next = NULL;
786
787 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +0300788 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +0300789
790
791 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +0300792 if (dev->dev_state != MEI_DEV_ENABLED) {
793 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300794 if (dev->init_clients_timer) {
795 if (--dev->init_clients_timer == 0) {
796 dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
797 dev->init_clients_state);
798 mei_reset(dev, 1);
799 }
800 }
801 }
802 goto out;
803 }
804 /*** connect/disconnect timeouts ***/
805 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
806 if (cl_pos->timer_count) {
807 if (--cl_pos->timer_count == 0) {
808 dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
809 mei_reset(dev, 1);
810 goto out;
811 }
812 }
813 }
814
Oren Weilfb7d8792011-05-15 13:43:42 +0300815 if (dev->iamthif_stall_timer) {
816 if (--dev->iamthif_stall_timer == 0) {
Masanari Iida32de21f2012-01-25 23:14:56 +0900817 dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +0300818 mei_reset(dev, 1);
819 dev->iamthif_msg_buf_size = 0;
820 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300821 dev->iamthif_canceled = false;
822 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300823 dev->iamthif_state = MEI_IAMTHIF_IDLE;
824 dev->iamthif_timer = 0;
825
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200826 mei_io_cb_free(dev->iamthif_current_cb);
827 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300828
829 dev->iamthif_file_object = NULL;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200830 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300831 }
832 }
833
834 if (dev->iamthif_timer) {
835
836 timeout = dev->iamthif_timer +
Tomas Winkler3870c322012-11-01 21:17:14 +0200837 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilfb7d8792011-05-15 13:43:42 +0300838
839 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
840 dev->iamthif_timer);
841 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
842 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
843 if (time_after(jiffies, timeout)) {
844 /*
845 * User didn't read the AMTHI data on time (15sec)
846 * freeing AMTHI for other requests
847 */
848
849 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
850
Tomas Winklere773efc2012-11-11 17:37:58 +0200851 list_for_each_entry_safe(cb_pos, cb_next,
852 &dev->amthif_rd_complete_list.list, list) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300853
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200854 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +0300855
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200856 /* Finding the AMTHI entry. */
857 if (cl_pos == &dev->iamthif_cl)
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200858 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300859 }
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200860 mei_io_cb_free(dev->iamthif_current_cb);
861 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300862
863 dev->iamthif_file_object->private_data = NULL;
864 dev->iamthif_file_object = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300865 dev->iamthif_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200866 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300867
868 }
869 }
870out:
Tomas Winkler441ab502011-12-13 23:39:34 +0200871 schedule_delayed_work(&dev->timer_work, 2 * HZ);
872 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +0300873}
874
875/**
876 * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
877 * processing.
878 *
879 * @irq: The irq number
880 * @dev_id: pointer to the device structure
881 *
882 * returns irqreturn_t
883 *
884 */
885irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
886{
887 struct mei_device *dev = (struct mei_device *) dev_id;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200888 struct mei_cl_cb complete_list;
Oren Weilfb7d8792011-05-15 13:43:42 +0300889 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
890 struct mei_cl *cl;
891 s32 slots;
892 int rets;
893 bool bus_message_received;
894
895
896 dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
897 /* initialize our complete list */
898 mutex_lock(&dev->device_lock);
Tomas Winkler0288c7c2011-06-06 10:44:34 +0300899 mei_io_list_init(&complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300900 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +0300901
902 /* Ack the interrupt here
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700903 * In case of MSI we don't go through the quick handler */
Tomas Winkler4f61a7a2011-07-14 20:11:25 +0300904 if (pci_dev_msi_enabled(dev->pdev))
Tomas Winkler3a65dd42012-12-25 19:06:06 +0200905 mei_clear_interrupts(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +0300906
Oren Weilfb7d8792011-05-15 13:43:42 +0300907 dev->me_hw_state = mei_mecsr_read(dev);
908
909 /* check if ME wants a reset */
910 if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
Tomas Winklerb210d752012-08-07 00:03:56 +0300911 dev->dev_state != MEI_DEV_RESETING &&
912 dev->dev_state != MEI_DEV_INITIALIZING) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300913 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
914 mei_reset(dev, 1);
915 mutex_unlock(&dev->device_lock);
916 return IRQ_HANDLED;
917 }
918
919 /* check if we need to start the dev */
920 if ((dev->host_hw_state & H_RDY) == 0) {
921 if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
922 dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
923 dev->host_hw_state |= (H_IE | H_IG | H_RDY);
924 mei_hcsr_set(dev);
Tomas Winklerb210d752012-08-07 00:03:56 +0300925 dev->dev_state = MEI_DEV_INIT_CLIENTS;
Oren Weilfb7d8792011-05-15 13:43:42 +0300926 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
927 /* link is established
928 * start sending messages.
929 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200930 mei_hbm_start_req(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300931 mutex_unlock(&dev->device_lock);
932 return IRQ_HANDLED;
933 } else {
934 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
935 mutex_unlock(&dev->device_lock);
936 return IRQ_HANDLED;
937 }
938 }
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700939 /* check slots available for reading */
Oren Weilfb7d8792011-05-15 13:43:42 +0300940 slots = mei_count_full_read_slots(dev);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200941 while (slots > 0) {
942 /* we have urgent data to send so break the read */
943 if (dev->wr_ext_msg.hdr.length)
944 break;
945 dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots);
Oren Weilfb7d8792011-05-15 13:43:42 +0300946 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
947 rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
948 if (rets)
949 goto end;
950 }
Tomas Winkler9a84d612012-11-18 15:13:18 +0200951 rets = mei_irq_thread_write_handler(dev, &complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300952end:
953 dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
954 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler726917f2012-06-25 23:46:28 +0300955 dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300956
957 bus_message_received = false;
958 if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
959 dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
960 bus_message_received = true;
961 }
962 mutex_unlock(&dev->device_lock);
963 if (bus_message_received) {
964 dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
965 wake_up_interruptible(&dev->wait_recvd_msg);
966 bus_message_received = false;
967 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200968 if (list_empty(&complete_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +0300969 return IRQ_HANDLED;
970
971
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200972 list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200973 cl = cb_pos->cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200974 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300975 if (cl) {
976 if (cl != &dev->iamthif_cl) {
977 dev_dbg(&dev->pdev->dev, "completing call back.\n");
978 _mei_cmpl(cl, cb_pos);
979 cb_pos = NULL;
980 } else if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +0200981 mei_amthif_complete(dev, cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +0300982 }
983 }
984 }
985 return IRQ_HANDLED;
986}