blob: d4312a8e139a92e0d7b79ea35dc1934b6828605b [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
160 if (mei_disconnect(dev, cl)) {
161 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 Winkler1ccb7b62012-03-14 14:39:42 +0200410 if (mei_send_flow_control(dev, cl)) {
411 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;
446 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
447 if (mei_connect(dev, cl)) {
448 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{
472 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 Winklerea3b5fb2012-11-18 15:13:16 +0200477 mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
478 mei_hdr->host_addr = cl->host_client_id;
479 mei_hdr->me_addr = cl->me_client_id;
480 mei_hdr->reserved = 0;
481
482 if (*slots >= msg_slots) {
483 mei_hdr->length = len;
Oren Weilfb7d8792011-05-15 13:43:42 +0300484 mei_hdr->msg_complete = 1;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200485 /* Split the message only if we can write the whole host buffer */
Tomas Winkler24aadc82012-06-25 23:46:27 +0300486 } else if (*slots == dev->hbuf_depth) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200487 msg_slots = *slots;
488 len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
489 mei_hdr->length = len;
Oren Weilfb7d8792011-05-15 13:43:42 +0300490 mei_hdr->msg_complete = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300491 } else {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200492 /* wait for next time the host buffer is empty */
493 return 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300494 }
495
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200496 dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
497 cb->request_buffer.size, cb->buf_idx);
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200498 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200499
500 *slots -= msg_slots;
501 if (mei_write_message(dev, mei_hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200502 cb->request_buffer.data + cb->buf_idx)) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200503 cl->status = -ENODEV;
504 list_move_tail(&cb->list, &cmpl_list->list);
505 return -ENODEV;
506 }
507
508 if (mei_flow_ctrl_reduce(dev, cl))
509 return -ENODEV;
510
511 cl->status = 0;
512 cb->buf_idx += mei_hdr->length;
513 if (mei_hdr->msg_complete)
514 list_move_tail(&cb->list, &dev->write_waiting_list.list);
515
Oren Weilfb7d8792011-05-15 13:43:42 +0300516 return 0;
517}
518
519/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300520 * mei_irq_thread_read_handler - bottom half read routine after ISR to
521 * handle the read processing.
522 *
523 * @cmpl_list: An instance of our list structure
524 * @dev: the device structure
525 * @slots: slots to read.
526 *
527 * returns 0 on success, <0 on failure.
528 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200529static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300530 struct mei_device *dev,
531 s32 *slots)
532{
533 struct mei_msg_hdr *mei_hdr;
534 struct mei_cl *cl_pos = NULL;
535 struct mei_cl *cl_next = NULL;
536 int ret = 0;
537
538 if (!dev->rd_msg_hdr) {
539 dev->rd_msg_hdr = mei_mecbrw_read(dev);
540 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
541 (*slots)--;
542 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
543 }
544 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200545 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Oren Weilfb7d8792011-05-15 13:43:42 +0300546
547 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
548 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
549 ret = -EBADMSG;
550 goto end;
551 }
552
553 if (mei_hdr->host_addr || mei_hdr->me_addr) {
554 list_for_each_entry_safe(cl_pos, cl_next,
555 &dev->file_list, link) {
556 dev_dbg(&dev->pdev->dev,
557 "list_for_each_entry_safe read host"
558 " client = %d, ME client = %d\n",
559 cl_pos->host_client_id,
560 cl_pos->me_client_id);
561 if (cl_pos->host_client_id == mei_hdr->host_addr &&
562 cl_pos->me_client_id == mei_hdr->me_addr)
563 break;
564 }
565
566 if (&cl_pos->link == &dev->file_list) {
567 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
568 ret = -EBADMSG;
569 goto end;
570 }
571 }
572 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
573 dev_dbg(&dev->pdev->dev,
574 "we can't read the message slots =%08x.\n",
575 *slots);
576 /* we can't read the message */
577 ret = -ERANGE;
578 goto end;
579 }
580
581 /* decide where to read the message too */
582 if (!mei_hdr->host_addr) {
583 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200584 mei_hbm_dispatch(dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300585 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
586 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
587 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
588 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
589 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200590
591 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Tomas Winkler19838fb2012-11-01 21:17:15 +0200592
593 ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300594 if (ret)
595 goto end;
Oren Weilfb7d8792011-05-15 13:43:42 +0300596 } else {
597 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
598 ret = mei_irq_thread_read_client_message(cmpl_list,
599 dev, mei_hdr);
600 if (ret)
601 goto end;
602
603 }
604
605 /* reset the number of slots and header */
606 *slots = mei_count_full_read_slots(dev);
607 dev->rd_msg_hdr = 0;
608
609 if (*slots == -EOVERFLOW) {
610 /* overflow - reset */
611 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
612 /* set the event since message has been read */
613 ret = -ERANGE;
614 goto end;
615 }
616end:
617 return ret;
618}
619
620
621/**
622 * mei_irq_thread_write_handler - bottom half write routine after
623 * ISR to handle the write processing.
624 *
Oren Weilfb7d8792011-05-15 13:43:42 +0300625 * @dev: the device structure
Tomas Winkler9a84d612012-11-18 15:13:18 +0200626 * @cmpl_list: An instance of our list structure
Oren Weilfb7d8792011-05-15 13:43:42 +0300627 *
628 * returns 0 on success, <0 on failure.
629 */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200630static int mei_irq_thread_write_handler(struct mei_device *dev,
631 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300632{
633
634 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200635 struct mei_cl_cb *pos = NULL, *next = NULL;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200636 struct mei_cl_cb *list;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200637 s32 slots;
Oren Weilfb7d8792011-05-15 13:43:42 +0300638 int ret;
639
Tomas Winkler726917f2012-06-25 23:46:28 +0300640 if (!mei_hbuf_is_empty(dev)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300641 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
642 return 0;
643 }
Tomas Winkler9a84d612012-11-18 15:13:18 +0200644 slots = mei_hbuf_empty_slots(dev);
645 if (slots <= 0)
Tomas Winkler7d5e0e52012-06-19 09:13:36 +0300646 return -EMSGSIZE;
647
Oren Weilfb7d8792011-05-15 13:43:42 +0300648 /* complete all waiting for write CB */
649 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
650
651 list = &dev->write_waiting_list;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200652 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200653 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200654 if (cl == NULL)
655 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +0300656
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200657 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200658 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200659 if (MEI_WRITING == cl->writing_state &&
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200660 pos->fop_type == MEI_FOP_WRITE &&
661 cl != &dev->iamthif_cl) {
Tomas Winkler483136e2012-07-04 19:24:54 +0300662 dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200663 cl->writing_state = MEI_WRITE_COMPLETE;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200664 list_add_tail(&pos->list, &cmpl_list->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200665 }
666 if (cl == &dev->iamthif_cl) {
667 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
668 if (dev->iamthif_flow_control_pending) {
Tomas Winkler9a84d612012-11-18 15:13:18 +0200669 ret = mei_amthif_irq_read(dev, &slots);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200670 if (ret)
671 return ret;
672 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300673 }
674 }
675
Tomas Winklerc216fde2012-08-16 19:39:43 +0300676 if (dev->wd_state == MEI_WD_STOPPING) {
677 dev->wd_state = MEI_WD_IDLE;
Oren Weilfb7d8792011-05-15 13:43:42 +0300678 wake_up_interruptible(&dev->wait_stop_wd);
Oren Weilfb7d8792011-05-15 13:43:42 +0300679 }
680
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200681 if (dev->wr_ext_msg.hdr.length) {
682 mei_write_message(dev, &dev->wr_ext_msg.hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200683 dev->wr_ext_msg.data);
Tomas Winkler9a84d612012-11-18 15:13:18 +0200684 slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200685 dev->wr_ext_msg.hdr.length = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300686 }
Tomas Winklerb210d752012-08-07 00:03:56 +0300687 if (dev->dev_state == MEI_DEV_ENABLED) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300688 if (dev->wd_pending &&
Tomas Winkler483136e2012-07-04 19:24:54 +0300689 mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300690 if (mei_wd_send(dev))
691 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
Tomas Winkler483136e2012-07-04 19:24:54 +0300692 else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl))
693 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300694
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300695 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300696
Tomas Winklerc216fde2012-08-16 19:39:43 +0300697 if (dev->wd_state == MEI_WD_RUNNING)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200698 slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300699 else
Tomas Winkler9a84d612012-11-18 15:13:18 +0200700 slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
Oren Weilfb7d8792011-05-15 13:43:42 +0300701 }
702 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300703
704 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +0200705 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200706 list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200707 cl = pos->cl;
Tomas Winklerc8372092011-11-27 21:43:33 +0200708 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200709 list_del(&pos->list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200710 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300711 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200712 switch (pos->fop_type) {
713 case MEI_FOP_CLOSE:
Tomas Winklerc8372092011-11-27 21:43:33 +0200714 /* send disconnect message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200715 ret = _mei_irq_thread_close(dev, &slots, pos,
716 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200717 if (ret)
718 return ret;
719
720 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200721 case MEI_FOP_READ:
Tomas Winklerc8372092011-11-27 21:43:33 +0200722 /* send flow control message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200723 ret = _mei_irq_thread_read(dev, &slots, pos,
724 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200725 if (ret)
726 return ret;
727
728 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200729 case MEI_FOP_IOCTL:
Tomas Winklerc8372092011-11-27 21:43:33 +0200730 /* connect message */
Natalia Ovsyanikove8cd29d2011-12-05 00:16:54 +0200731 if (mei_other_client_is_connecting(dev, cl))
Tomas Winklerc8372092011-11-27 21:43:33 +0200732 continue;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200733 ret = _mei_irq_thread_ioctl(dev, &slots, pos,
734 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200735 if (ret)
736 return ret;
737
738 break;
739
740 default:
741 BUG();
742 }
743
Oren Weilfb7d8792011-05-15 13:43:42 +0300744 }
745 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200746 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200747 list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200748 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200749 if (cl == NULL)
750 continue;
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200751 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
752 dev_dbg(&dev->pdev->dev,
753 "No flow control credentials for client %d, not sending.\n",
754 cl->host_client_id);
755 continue;
756 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300757
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200758 if (cl == &dev->iamthif_cl)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200759 ret = mei_amthif_irq_write_complete(dev, &slots,
Tomas Winkler24c656e2012-11-18 15:13:17 +0200760 pos, cmpl_list);
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200761 else
762 ret = mei_irq_thread_write_complete(dev, &slots, pos,
763 cmpl_list);
764 if (ret)
765 return ret;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200766
Oren Weilfb7d8792011-05-15 13:43:42 +0300767 }
768 return 0;
769}
770
771
772
773/**
774 * mei_timer - timer function.
775 *
776 * @work: pointer to the work_struct structure
777 *
778 * NOTE: This function is called by timer interrupt work
779 */
Oren Weila61c6532011-09-07 09:03:13 +0300780void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +0300781{
782 unsigned long timeout;
783 struct mei_cl *cl_pos = NULL;
784 struct mei_cl *cl_next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300785 struct mei_cl_cb *cb_pos = NULL;
786 struct mei_cl_cb *cb_next = NULL;
787
788 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +0300789 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +0300790
791
792 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +0300793 if (dev->dev_state != MEI_DEV_ENABLED) {
794 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300795 if (dev->init_clients_timer) {
796 if (--dev->init_clients_timer == 0) {
797 dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
798 dev->init_clients_state);
799 mei_reset(dev, 1);
800 }
801 }
802 }
803 goto out;
804 }
805 /*** connect/disconnect timeouts ***/
806 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
807 if (cl_pos->timer_count) {
808 if (--cl_pos->timer_count == 0) {
809 dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
810 mei_reset(dev, 1);
811 goto out;
812 }
813 }
814 }
815
Oren Weilfb7d8792011-05-15 13:43:42 +0300816 if (dev->iamthif_stall_timer) {
817 if (--dev->iamthif_stall_timer == 0) {
Masanari Iida32de21f2012-01-25 23:14:56 +0900818 dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +0300819 mei_reset(dev, 1);
820 dev->iamthif_msg_buf_size = 0;
821 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300822 dev->iamthif_canceled = false;
823 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300824 dev->iamthif_state = MEI_IAMTHIF_IDLE;
825 dev->iamthif_timer = 0;
826
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200827 mei_io_cb_free(dev->iamthif_current_cb);
828 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300829
830 dev->iamthif_file_object = NULL;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200831 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300832 }
833 }
834
835 if (dev->iamthif_timer) {
836
837 timeout = dev->iamthif_timer +
Tomas Winkler3870c322012-11-01 21:17:14 +0200838 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilfb7d8792011-05-15 13:43:42 +0300839
840 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
841 dev->iamthif_timer);
842 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
843 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
844 if (time_after(jiffies, timeout)) {
845 /*
846 * User didn't read the AMTHI data on time (15sec)
847 * freeing AMTHI for other requests
848 */
849
850 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
851
Tomas Winklere773efc2012-11-11 17:37:58 +0200852 list_for_each_entry_safe(cb_pos, cb_next,
853 &dev->amthif_rd_complete_list.list, list) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300854
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200855 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +0300856
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200857 /* Finding the AMTHI entry. */
858 if (cl_pos == &dev->iamthif_cl)
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200859 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300860 }
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200861 mei_io_cb_free(dev->iamthif_current_cb);
862 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300863
864 dev->iamthif_file_object->private_data = NULL;
865 dev->iamthif_file_object = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300866 dev->iamthif_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200867 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300868
869 }
870 }
871out:
Tomas Winkler441ab502011-12-13 23:39:34 +0200872 schedule_delayed_work(&dev->timer_work, 2 * HZ);
873 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +0300874}
875
876/**
877 * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
878 * processing.
879 *
880 * @irq: The irq number
881 * @dev_id: pointer to the device structure
882 *
883 * returns irqreturn_t
884 *
885 */
886irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
887{
888 struct mei_device *dev = (struct mei_device *) dev_id;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200889 struct mei_cl_cb complete_list;
Oren Weilfb7d8792011-05-15 13:43:42 +0300890 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
891 struct mei_cl *cl;
892 s32 slots;
893 int rets;
894 bool bus_message_received;
895
896
897 dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
898 /* initialize our complete list */
899 mutex_lock(&dev->device_lock);
Tomas Winkler0288c7c2011-06-06 10:44:34 +0300900 mei_io_list_init(&complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300901 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +0300902
903 /* Ack the interrupt here
Justin P. Mattock5f9092f2012-03-12 07:18:09 -0700904 * In case of MSI we don't go through the quick handler */
Tomas Winkler4f61a7a2011-07-14 20:11:25 +0300905 if (pci_dev_msi_enabled(dev->pdev))
Tomas Winkler3a65dd42012-12-25 19:06:06 +0200906 mei_clear_interrupts(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +0300907
Oren Weilfb7d8792011-05-15 13:43:42 +0300908 dev->me_hw_state = mei_mecsr_read(dev);
909
910 /* check if ME wants a reset */
911 if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
Tomas Winklerb210d752012-08-07 00:03:56 +0300912 dev->dev_state != MEI_DEV_RESETING &&
913 dev->dev_state != MEI_DEV_INITIALIZING) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300914 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
915 mei_reset(dev, 1);
916 mutex_unlock(&dev->device_lock);
917 return IRQ_HANDLED;
918 }
919
920 /* check if we need to start the dev */
921 if ((dev->host_hw_state & H_RDY) == 0) {
922 if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
923 dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
924 dev->host_hw_state |= (H_IE | H_IG | H_RDY);
925 mei_hcsr_set(dev);
Tomas Winklerb210d752012-08-07 00:03:56 +0300926 dev->dev_state = MEI_DEV_INIT_CLIENTS;
Oren Weilfb7d8792011-05-15 13:43:42 +0300927 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
928 /* link is established
929 * start sending messages.
930 */
Tomas Winklerc95efb72011-05-25 17:28:21 +0300931 mei_host_start_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300932 mutex_unlock(&dev->device_lock);
933 return IRQ_HANDLED;
934 } else {
935 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
936 mutex_unlock(&dev->device_lock);
937 return IRQ_HANDLED;
938 }
939 }
Justin P. Mattock5f9092f2012-03-12 07:18:09 -0700940 /* check slots available for reading */
Oren Weilfb7d8792011-05-15 13:43:42 +0300941 slots = mei_count_full_read_slots(dev);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200942 while (slots > 0) {
943 /* we have urgent data to send so break the read */
944 if (dev->wr_ext_msg.hdr.length)
945 break;
946 dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots);
Oren Weilfb7d8792011-05-15 13:43:42 +0300947 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
948 rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
949 if (rets)
950 goto end;
951 }
Tomas Winkler9a84d612012-11-18 15:13:18 +0200952 rets = mei_irq_thread_write_handler(dev, &complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300953end:
954 dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
955 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler726917f2012-06-25 23:46:28 +0300956 dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300957
958 bus_message_received = false;
959 if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
960 dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
961 bus_message_received = true;
962 }
963 mutex_unlock(&dev->device_lock);
964 if (bus_message_received) {
965 dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
966 wake_up_interruptible(&dev->wait_recvd_msg);
967 bus_message_received = false;
968 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200969 if (list_empty(&complete_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +0300970 return IRQ_HANDLED;
971
972
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200973 list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200974 cl = cb_pos->cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200975 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300976 if (cl) {
977 if (cl != &dev->iamthif_cl) {
978 dev_dbg(&dev->pdev->dev, "completing call back.\n");
979 _mei_cmpl(cl, cb_pos);
980 cb_pos = NULL;
981 } else if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +0200982 mei_amthif_complete(dev, cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +0300983 }
984 }
985 }
986 return IRQ_HANDLED;
987}