blob: b72fa8196ddb4358727073e1d9a864cd61ca8b52 [file] [log] [blame]
Oren Weilfb7d8792011-05-15 13:43:42 +03001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
Tomas Winkler733ba91c2012-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
24#include "mei_dev.h"
Tomas Winkler4f3afe12012-05-09 16:38:59 +030025#include <linux/mei.h>
Oren Weilfb7d8792011-05-15 13:43:42 +030026#include "hw.h"
27#include "interface.h"
28
29
30/**
31 * mei_interrupt_quick_handler - The ISR of the MEI device
32 *
33 * @irq: The irq number
34 * @dev_id: pointer to the device structure
35 *
36 * returns irqreturn_t
37 */
38irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id)
39{
40 struct mei_device *dev = (struct mei_device *) dev_id;
41 u32 csr_reg = mei_hcsr_read(dev);
42
43 if ((csr_reg & H_IS) != H_IS)
44 return IRQ_NONE;
45
46 /* clear H_IS bit in H_CSR */
47 mei_reg_write(dev, H_CSR, csr_reg);
48
49 return IRQ_WAKE_THREAD;
50}
51
52/**
53 * _mei_cmpl - processes completed operation.
54 *
55 * @cl: private data of the file object.
56 * @cb_pos: callback block.
57 */
58static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
59{
Tomas Winkler4b8960b2012-11-11 17:38:00 +020060 if (cb_pos->fop_type == MEI_FOP_WRITE) {
Tomas Winkler601a1ef2012-10-09 16:50:20 +020061 mei_io_cb_free(cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +030062 cb_pos = NULL;
63 cl->writing_state = MEI_WRITE_COMPLETE;
64 if (waitqueue_active(&cl->tx_wait))
65 wake_up_interruptible(&cl->tx_wait);
66
Tomas Winkler4b8960b2012-11-11 17:38:00 +020067 } else if (cb_pos->fop_type == MEI_FOP_READ &&
Oren Weilfb7d8792011-05-15 13:43:42 +030068 MEI_READING == cl->reading_state) {
69 cl->reading_state = MEI_READ_COMPLETE;
70 if (waitqueue_active(&cl->rx_wait))
71 wake_up_interruptible(&cl->rx_wait);
72
73 }
74}
75
76/**
Oren Weilfb7d8792011-05-15 13:43:42 +030077 * _mei_irq_thread_state_ok - checks if mei header matches file private data
78 *
79 * @cl: private data of the file object
80 * @mei_hdr: header of mei client message
81 *
82 * returns !=0 if matches, 0 if no match.
83 */
84static int _mei_irq_thread_state_ok(struct mei_cl *cl,
85 struct mei_msg_hdr *mei_hdr)
86{
87 return (cl->host_client_id == mei_hdr->host_addr &&
88 cl->me_client_id == mei_hdr->me_addr &&
89 cl->state == MEI_FILE_CONNECTED &&
90 MEI_READ_COMPLETE != cl->reading_state);
91}
92
93/**
94 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
95 * handle the read mei client message data processing.
96 *
97 * @complete_list: An instance of our list structure
98 * @dev: the device structure
99 * @mei_hdr: header of mei client message
100 *
101 * returns 0 on success, <0 on failure.
102 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200103static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300104 struct mei_device *dev,
105 struct mei_msg_hdr *mei_hdr)
106{
107 struct mei_cl *cl;
108 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
Tomas Winkler479bc592011-06-16 00:46:03 +0300109 unsigned char *buffer = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300110
111 dev_dbg(&dev->pdev->dev, "start client msg\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200112 if (list_empty(&dev->read_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +0300113 goto quit;
114
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200115 list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200116 cl = cb_pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300117 if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
118 cl->reading_state = MEI_READING;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200119 buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +0300120
121 if (cb_pos->response_buffer.size <
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200122 mei_hdr->length + cb_pos->buf_idx) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300123 dev_dbg(&dev->pdev->dev, "message overflow.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200124 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300125 return -ENOMEM;
126 }
127 if (buffer)
128 mei_read_slots(dev, buffer, mei_hdr->length);
129
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200130 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300131 if (mei_hdr->msg_complete) {
132 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200133 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300134 dev_dbg(&dev->pdev->dev,
Tomas Winklera4136b42012-09-11 00:43:22 +0300135 "completed read H cl = %d, ME cl = %d, length = %lu\n",
Oren Weilfb7d8792011-05-15 13:43:42 +0300136 cl->host_client_id,
137 cl->me_client_id,
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200138 cb_pos->buf_idx);
139
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200140 list_add_tail(&cb_pos->list,
141 &complete_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300142 }
143
144 break;
145 }
146
147 }
148
149quit:
150 dev_dbg(&dev->pdev->dev, "message read\n");
151 if (!buffer) {
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200152 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
Oren Weilfb7d8792011-05-15 13:43:42 +0300153 dev_dbg(&dev->pdev->dev, "discarding message, header =%08x.\n",
154 *(u32 *) dev->rd_msg_buf);
155 }
156
157 return 0;
158}
159
160/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300161 * _mei_irq_thread_close - processes close related operation.
162 *
163 * @dev: the device structure.
164 * @slots: free slots.
165 * @cb_pos: callback block.
166 * @cl: private data of the file object.
167 * @cmpl_list: complete list.
168 *
169 * returns 0, OK; otherwise, error.
170 */
171static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
172 struct mei_cl_cb *cb_pos,
173 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200174 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300175{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300176 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200177 sizeof(struct hbm_client_connect_request)))
Oren Weilfb7d8792011-05-15 13:43:42 +0300178 return -EBADMSG;
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300179
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200180 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300181
182 if (mei_disconnect(dev, cl)) {
183 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200184 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200185 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300186 return -EMSGSIZE;
187 } else {
188 cl->state = MEI_FILE_DISCONNECTING;
189 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200190 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200191 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300192 cl->timer_count = MEI_CONNECT_TIMEOUT;
Oren Weilfb7d8792011-05-15 13:43:42 +0300193 }
194
195 return 0;
196}
197
198/**
199 * is_treat_specially_client - checks if the message belongs
200 * to the file private data.
201 *
202 * @cl: private data of the file object
203 * @rs: connect response bus message
204 *
205 */
206static bool is_treat_specially_client(struct mei_cl *cl,
207 struct hbm_client_connect_response *rs)
208{
209
210 if (cl->host_client_id == rs->host_addr &&
211 cl->me_client_id == rs->me_addr) {
212 if (!rs->status) {
213 cl->state = MEI_FILE_CONNECTED;
214 cl->status = 0;
215
216 } else {
217 cl->state = MEI_FILE_DISCONNECTED;
218 cl->status = -ENODEV;
219 }
220 cl->timer_count = 0;
221
222 return true;
223 }
224 return false;
225}
226
227/**
228 * mei_client_connect_response - connects to response irq routine
229 *
230 * @dev: the device structure
231 * @rs: connect response bus message
232 */
233static void mei_client_connect_response(struct mei_device *dev,
234 struct hbm_client_connect_response *rs)
235{
236
237 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200238 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300239
240 dev_dbg(&dev->pdev->dev,
241 "connect_response:\n"
242 "ME Client = %d\n"
243 "Host Client = %d\n"
244 "Status = %d\n",
245 rs->me_addr,
246 rs->host_addr,
247 rs->status);
248
249 /* if WD or iamthif client treat specially */
250
251 if (is_treat_specially_client(&(dev->wd_cl), rs)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300252 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
Tomas Winkler70cd5332011-12-22 18:50:50 +0200253 mei_watchdog_register(dev);
Oren Weil9ce178e2011-09-07 09:03:09 +0300254
Oren Weilfb7d8792011-05-15 13:43:42 +0300255 return;
256 }
257
258 if (is_treat_specially_client(&(dev->iamthif_cl), rs)) {
259 dev->iamthif_state = MEI_IAMTHIF_IDLE;
260 return;
261 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200262 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200263
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200264 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200265 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200266 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200267 return;
268 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200269 if (pos->fop_type == MEI_FOP_IOCTL) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200270 if (is_treat_specially_client(cl, rs)) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200271 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200272 cl->status = 0;
273 cl->timer_count = 0;
274 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300275 }
276 }
277 }
278}
279
280/**
281 * mei_client_disconnect_response - disconnects from response irq routine
282 *
283 * @dev: the device structure
284 * @rs: disconnect response bus message
285 */
286static void mei_client_disconnect_response(struct mei_device *dev,
287 struct hbm_client_connect_response *rs)
288{
289 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200290 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300291
292 dev_dbg(&dev->pdev->dev,
293 "disconnect_response:\n"
294 "ME Client = %d\n"
295 "Host Client = %d\n"
296 "Status = %d\n",
297 rs->me_addr,
298 rs->host_addr,
299 rs->status);
300
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200301 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200302 cl = pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300303
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200304 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200305 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200306 return;
307 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300308
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200309 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
310 if (cl->host_client_id == rs->host_addr &&
311 cl->me_client_id == rs->me_addr) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300312
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200313 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200314 if (!rs->status)
315 cl->state = MEI_FILE_DISCONNECTED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300316
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200317 cl->status = 0;
318 cl->timer_count = 0;
319 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300320 }
321 }
322}
323
324/**
325 * same_flow_addr - tells if they have the same address.
326 *
327 * @file: private data of the file object.
328 * @flow: flow control.
329 *
330 * returns !=0, same; 0,not.
331 */
332static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow)
333{
334 return (cl->host_client_id == flow->host_addr &&
335 cl->me_client_id == flow->me_addr);
336}
337
338/**
339 * add_single_flow_creds - adds single buffer credentials.
340 *
341 * @file: private data ot the file object.
342 * @flow: flow control.
343 */
344static void add_single_flow_creds(struct mei_device *dev,
345 struct hbm_flow_control *flow)
346{
347 struct mei_me_client *client;
348 int i;
349
Tomas Winklercf9673d2011-06-06 10:44:33 +0300350 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300351 client = &dev->me_clients[i];
352 if (client && flow->me_addr == client->client_id) {
353 if (client->props.single_recv_buf) {
354 client->mei_flow_ctrl_creds++;
355 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
356 flow->me_addr);
357 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
358 client->mei_flow_ctrl_creds);
359 } else {
360 BUG(); /* error in flow control */
361 }
362 }
363 }
364}
365
366/**
367 * mei_client_flow_control_response - flow control response irq routine
368 *
369 * @dev: the device structure
370 * @flow_control: flow control response bus message
371 */
372static void mei_client_flow_control_response(struct mei_device *dev,
373 struct hbm_flow_control *flow_control)
374{
375 struct mei_cl *cl_pos = NULL;
376 struct mei_cl *cl_next = NULL;
377
378 if (!flow_control->host_addr) {
379 /* single receive buffer */
380 add_single_flow_creds(dev, flow_control);
381 } else {
382 /* normal connection */
383 list_for_each_entry_safe(cl_pos, cl_next,
384 &dev->file_list, link) {
385 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n");
386
387 dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n",
388 cl_pos->host_client_id,
389 cl_pos->me_client_id);
390 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
391 flow_control->host_addr,
392 flow_control->me_addr);
393 if (same_flow_addr(cl_pos, flow_control)) {
394 dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n",
395 flow_control->host_addr,
396 flow_control->me_addr);
397 cl_pos->mei_flow_ctrl_creds++;
398 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
399 cl_pos->mei_flow_ctrl_creds);
400 break;
401 }
402 }
403 }
404}
405
406/**
407 * same_disconn_addr - tells if they have the same address
408 *
409 * @file: private data of the file object.
410 * @disconn: disconnection request.
411 *
412 * returns !=0, same; 0,not.
413 */
414static int same_disconn_addr(struct mei_cl *cl,
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200415 struct hbm_client_connect_request *req)
Oren Weilfb7d8792011-05-15 13:43:42 +0300416{
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200417 return (cl->host_client_id == req->host_addr &&
418 cl->me_client_id == req->me_addr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300419}
420
421/**
422 * mei_client_disconnect_request - disconnects from request irq routine
423 *
424 * @dev: the device structure.
425 * @disconnect_req: disconnect request bus message.
426 */
427static void mei_client_disconnect_request(struct mei_device *dev,
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200428 struct hbm_client_connect_request *disconnect_req)
Oren Weilfb7d8792011-05-15 13:43:42 +0300429{
Oren Weilfb7d8792011-05-15 13:43:42 +0300430 struct hbm_client_connect_response *disconnect_res;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200431 struct mei_cl *pos, *next;
432 const size_t len = sizeof(struct hbm_client_connect_response);
Oren Weilfb7d8792011-05-15 13:43:42 +0300433
Tomas Winkler5bd64712012-11-18 15:13:14 +0200434 list_for_each_entry_safe(pos, next, &dev->file_list, link) {
435 if (same_disconn_addr(pos, disconnect_req)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300436 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
437 disconnect_req->host_addr,
438 disconnect_req->me_addr);
Tomas Winkler5bd64712012-11-18 15:13:14 +0200439 pos->state = MEI_FILE_DISCONNECTED;
440 pos->timer_count = 0;
441 if (pos == &dev->wd_cl)
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300442 dev->wd_pending = false;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200443 else if (pos == &dev->iamthif_cl)
Oren Weilfb7d8792011-05-15 13:43:42 +0300444 dev->iamthif_timer = 0;
445
446 /* prepare disconnect response */
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200447 (void)mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
Oren Weilfb7d8792011-05-15 13:43:42 +0300448 disconnect_res =
449 (struct hbm_client_connect_response *)
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200450 &dev->wr_ext_msg.data;
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200451 disconnect_res->hbm_cmd = CLIENT_DISCONNECT_RES_CMD;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200452 disconnect_res->host_addr = pos->host_client_id;
453 disconnect_res->me_addr = pos->me_client_id;
Oren Weilfb7d8792011-05-15 13:43:42 +0300454 disconnect_res->status = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300455 break;
456 }
457 }
458}
459
Oren Weilfb7d8792011-05-15 13:43:42 +0300460/**
461 * mei_irq_thread_read_bus_message - bottom half read routine after ISR to
462 * handle the read bus message cmd processing.
463 *
464 * @dev: the device structure
465 * @mei_hdr: header of bus message
466 */
467static void mei_irq_thread_read_bus_message(struct mei_device *dev,
Tomas Winkler438763f2012-12-25 19:05:59 +0200468 struct mei_msg_hdr *hdr)
Oren Weilfb7d8792011-05-15 13:43:42 +0300469{
470 struct mei_bus_message *mei_msg;
Samuel Ortizc1174c02012-11-18 15:13:20 +0200471 struct mei_me_client *me_client;
Oren Weilfb7d8792011-05-15 13:43:42 +0300472 struct hbm_host_version_response *version_res;
473 struct hbm_client_connect_response *connect_res;
474 struct hbm_client_connect_response *disconnect_res;
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200475 struct hbm_client_connect_request *disconnect_req;
Oren Weilfb7d8792011-05-15 13:43:42 +0300476 struct hbm_flow_control *flow_control;
477 struct hbm_props_response *props_res;
478 struct hbm_host_enum_response *enum_res;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200479 struct hbm_host_stop_request *stop_req;
Oren Weilfb7d8792011-05-15 13:43:42 +0300480
481 /* read the message to our buffer */
Tomas Winkler438763f2012-12-25 19:05:59 +0200482 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
483 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200484 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
Oren Weilfb7d8792011-05-15 13:43:42 +0300485
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200486 switch (mei_msg->hbm_cmd) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300487 case HOST_START_RES_CMD:
488 version_res = (struct hbm_host_version_response *) mei_msg;
489 if (version_res->host_version_supported) {
490 dev->version.major_version = HBM_MAJOR_VERSION;
491 dev->version.minor_version = HBM_MINOR_VERSION;
Tomas Winklerb210d752012-08-07 00:03:56 +0300492 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300493 dev->init_clients_state == MEI_START_MESSAGE) {
494 dev->init_clients_timer = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300495 mei_host_enum_clients_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300496 } else {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300497 dev->recvd_msg = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300498 dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n");
499 mei_reset(dev, 1);
500 return;
501 }
502 } else {
Tomas Winkler5bd64712012-11-18 15:13:14 +0200503 u32 *buf = dev->wr_msg_buf;
504 const size_t len = sizeof(struct hbm_host_stop_request);
505
Oren Weilfb7d8792011-05-15 13:43:42 +0300506 dev->version = version_res->me_max_version;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200507
Oren Weilfb7d8792011-05-15 13:43:42 +0300508 /* send stop message */
Tomas Winkler438763f2012-12-25 19:05:59 +0200509 hdr = mei_hbm_hdr(&buf[0], len);
Tomas Winkler5bd64712012-11-18 15:13:14 +0200510 stop_req = (struct hbm_host_stop_request *)&buf[1];
511 memset(stop_req, 0, len);
512 stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
513 stop_req->reason = DRIVER_STOP_REQUEST;
Oren Weilfb7d8792011-05-15 13:43:42 +0300514
Tomas Winkler438763f2012-12-25 19:05:59 +0200515 mei_write_message(dev, hdr, (unsigned char *)stop_req);
Oren Weilfb7d8792011-05-15 13:43:42 +0300516 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
517 return;
518 }
519
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300520 dev->recvd_msg = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300521 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
522 break;
523
524 case CLIENT_CONNECT_RES_CMD:
Tomas Winkler5bd64712012-11-18 15:13:14 +0200525 connect_res = (struct hbm_client_connect_response *) mei_msg;
Oren Weilfb7d8792011-05-15 13:43:42 +0300526 mei_client_connect_response(dev, connect_res);
527 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
528 wake_up(&dev->wait_recvd_msg);
529 break;
530
531 case CLIENT_DISCONNECT_RES_CMD:
Tomas Winkler5bd64712012-11-18 15:13:14 +0200532 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler441ab502011-12-13 23:39:34 +0200533 mei_client_disconnect_response(dev, disconnect_res);
Oren Weilfb7d8792011-05-15 13:43:42 +0300534 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
535 wake_up(&dev->wait_recvd_msg);
536 break;
537
538 case MEI_FLOW_CONTROL_CMD:
539 flow_control = (struct hbm_flow_control *) mei_msg;
540 mei_client_flow_control_response(dev, flow_control);
541 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
542 break;
543
544 case HOST_CLIENT_PROPERTIES_RES_CMD:
545 props_res = (struct hbm_props_response *)mei_msg;
Samuel Ortizc1174c02012-11-18 15:13:20 +0200546 me_client = &dev->me_clients[dev->me_client_presentation_num];
547
Oren Weilfb7d8792011-05-15 13:43:42 +0300548 if (props_res->status || !dev->me_clients) {
549 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
550 mei_reset(dev, 1);
551 return;
552 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300553
Samuel Ortizc1174c02012-11-18 15:13:20 +0200554 if (me_client->client_id != props_res->address) {
555 dev_err(&dev->pdev->dev,
556 "Host client properties reply mismatch\n");
Oren Weilfb7d8792011-05-15 13:43:42 +0300557 mei_reset(dev, 1);
Samuel Ortizc1174c02012-11-18 15:13:20 +0200558
Oren Weilfb7d8792011-05-15 13:43:42 +0300559 return;
560 }
Samuel Ortizc1174c02012-11-18 15:13:20 +0200561
562 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
563 dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
564 dev_err(&dev->pdev->dev,
565 "Unexpected client properties reply\n");
566 mei_reset(dev, 1);
567
568 return;
569 }
570
571 me_client->props = props_res->client_properties;
572 dev->me_client_index++;
573 dev->me_client_presentation_num++;
574
575 mei_host_client_enumerate(dev);
576
Oren Weilfb7d8792011-05-15 13:43:42 +0300577 break;
578
579 case HOST_ENUM_RES_CMD:
580 enum_res = (struct hbm_host_enum_response *) mei_msg;
581 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
Tomas Winklerb210d752012-08-07 00:03:56 +0300582 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300583 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
584 dev->init_clients_timer = 0;
585 dev->me_client_presentation_num = 0;
586 dev->me_client_index = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300587 mei_allocate_me_clients_storage(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300588 dev->init_clients_state =
589 MEI_CLIENT_PROPERTIES_MESSAGE;
Samuel Ortizc1174c02012-11-18 15:13:20 +0200590
591 mei_host_client_enumerate(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300592 } else {
593 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
594 mei_reset(dev, 1);
595 return;
596 }
597 break;
598
599 case HOST_STOP_RES_CMD:
Tomas Winklerb210d752012-08-07 00:03:56 +0300600 dev->dev_state = MEI_DEV_DISABLED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300601 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
602 mei_reset(dev, 1);
603 break;
604
605 case CLIENT_DISCONNECT_REQ_CMD:
606 /* search for client */
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200607 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Oren Weilfb7d8792011-05-15 13:43:42 +0300608 mei_client_disconnect_request(dev, disconnect_req);
609 break;
610
611 case ME_STOP_REQ_CMD:
Tomas Winkler5bd64712012-11-18 15:13:14 +0200612 {
613 /* prepare stop request: sent in next interrupt event */
614
Tomas Winkler5bd64712012-11-18 15:13:14 +0200615 const size_t len = sizeof(struct hbm_host_stop_request);
616
Tomas Winkler438763f2012-12-25 19:05:59 +0200617 hdr = mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200618 stop_req = (struct hbm_host_stop_request *)&dev->wr_ext_msg.data;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200619 memset(stop_req, 0, len);
620 stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
621 stop_req->reason = DRIVER_STOP_REQUEST;
Oren Weilfb7d8792011-05-15 13:43:42 +0300622 break;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200623 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300624 default:
625 BUG();
626 break;
627
628 }
629}
630
631
632/**
633 * _mei_hb_read - processes read related operation.
634 *
635 * @dev: the device structure.
636 * @slots: free slots.
637 * @cb_pos: callback block.
638 * @cl: private data of the file object.
639 * @cmpl_list: complete list.
640 *
641 * returns 0, OK; otherwise, error.
642 */
643static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
644 struct mei_cl_cb *cb_pos,
645 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200646 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300647{
Tomas Winkler1e69d642012-05-29 16:39:12 +0300648 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300649 sizeof(struct hbm_flow_control))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300650 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200651 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300652 return -EBADMSG;
653 }
654
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300655 *slots -= mei_data2slots(sizeof(struct hbm_flow_control));
656
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200657 if (mei_send_flow_control(dev, cl)) {
658 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200659 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200660 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200661 return -ENODEV;
662 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200663 list_move_tail(&cb_pos->list, &dev->read_list.list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200664
Oren Weilfb7d8792011-05-15 13:43:42 +0300665 return 0;
666}
667
668
669/**
670 * _mei_irq_thread_ioctl - processes ioctl related operation.
671 *
672 * @dev: the device structure.
673 * @slots: free slots.
674 * @cb_pos: callback block.
675 * @cl: private data of the file object.
676 * @cmpl_list: complete list.
677 *
678 * returns 0, OK; otherwise, error.
679 */
680static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
681 struct mei_cl_cb *cb_pos,
682 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200683 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300684{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300685 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300686 sizeof(struct hbm_client_connect_request))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300687 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200688 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300689 return -EBADMSG;
690 }
691
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300692 cl->state = MEI_FILE_CONNECTING;
693 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
694 if (mei_connect(dev, cl)) {
695 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200696 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200697 list_del(&cb_pos->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300698 return -ENODEV;
699 } else {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200700 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300701 cl->timer_count = MEI_CONNECT_TIMEOUT;
702 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300703 return 0;
704}
705
706/**
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200707 * mei_irq_thread_write_complete - write messages to device.
Oren Weilfb7d8792011-05-15 13:43:42 +0300708 *
709 * @dev: the device structure.
710 * @slots: free slots.
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200711 * @cb: callback block.
Oren Weilfb7d8792011-05-15 13:43:42 +0300712 * @cmpl_list: complete list.
713 *
714 * returns 0, OK; otherwise, error.
715 */
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200716static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
717 struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300718{
719 struct mei_msg_hdr *mei_hdr;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200720 struct mei_cl *cl = cb->cl;
721 size_t len = cb->request_buffer.size - cb->buf_idx;
722 size_t msg_slots = mei_data2slots(len);
Oren Weilfb7d8792011-05-15 13:43:42 +0300723
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200724 mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
725 mei_hdr->host_addr = cl->host_client_id;
726 mei_hdr->me_addr = cl->me_client_id;
727 mei_hdr->reserved = 0;
728
729 if (*slots >= msg_slots) {
730 mei_hdr->length = len;
Oren Weilfb7d8792011-05-15 13:43:42 +0300731 mei_hdr->msg_complete = 1;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200732 /* Split the message only if we can write the whole host buffer */
Tomas Winkler24aadc82012-06-25 23:46:27 +0300733 } else if (*slots == dev->hbuf_depth) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200734 msg_slots = *slots;
735 len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
736 mei_hdr->length = len;
Oren Weilfb7d8792011-05-15 13:43:42 +0300737 mei_hdr->msg_complete = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300738 } else {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200739 /* wait for next time the host buffer is empty */
740 return 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300741 }
742
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200743 dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
744 cb->request_buffer.size, cb->buf_idx);
745 dev_dbg(&dev->pdev->dev, "msg: len = %d complete = %d\n",
746 mei_hdr->length, mei_hdr->msg_complete);
747
748 *slots -= msg_slots;
749 if (mei_write_message(dev, mei_hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200750 cb->request_buffer.data + cb->buf_idx)) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200751 cl->status = -ENODEV;
752 list_move_tail(&cb->list, &cmpl_list->list);
753 return -ENODEV;
754 }
755
756 if (mei_flow_ctrl_reduce(dev, cl))
757 return -ENODEV;
758
759 cl->status = 0;
760 cb->buf_idx += mei_hdr->length;
761 if (mei_hdr->msg_complete)
762 list_move_tail(&cb->list, &dev->write_waiting_list.list);
763
Oren Weilfb7d8792011-05-15 13:43:42 +0300764 return 0;
765}
766
767/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300768 * mei_irq_thread_read_handler - bottom half read routine after ISR to
769 * handle the read processing.
770 *
771 * @cmpl_list: An instance of our list structure
772 * @dev: the device structure
773 * @slots: slots to read.
774 *
775 * returns 0 on success, <0 on failure.
776 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200777static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300778 struct mei_device *dev,
779 s32 *slots)
780{
781 struct mei_msg_hdr *mei_hdr;
782 struct mei_cl *cl_pos = NULL;
783 struct mei_cl *cl_next = NULL;
784 int ret = 0;
785
786 if (!dev->rd_msg_hdr) {
787 dev->rd_msg_hdr = mei_mecbrw_read(dev);
788 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
789 (*slots)--;
790 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
791 }
792 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
793 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", mei_hdr->length);
794
795 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
796 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
797 ret = -EBADMSG;
798 goto end;
799 }
800
801 if (mei_hdr->host_addr || mei_hdr->me_addr) {
802 list_for_each_entry_safe(cl_pos, cl_next,
803 &dev->file_list, link) {
804 dev_dbg(&dev->pdev->dev,
805 "list_for_each_entry_safe read host"
806 " client = %d, ME client = %d\n",
807 cl_pos->host_client_id,
808 cl_pos->me_client_id);
809 if (cl_pos->host_client_id == mei_hdr->host_addr &&
810 cl_pos->me_client_id == mei_hdr->me_addr)
811 break;
812 }
813
814 if (&cl_pos->link == &dev->file_list) {
815 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
816 ret = -EBADMSG;
817 goto end;
818 }
819 }
820 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
821 dev_dbg(&dev->pdev->dev,
822 "we can't read the message slots =%08x.\n",
823 *slots);
824 /* we can't read the message */
825 ret = -ERANGE;
826 goto end;
827 }
828
829 /* decide where to read the message too */
830 if (!mei_hdr->host_addr) {
831 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
832 mei_irq_thread_read_bus_message(dev, mei_hdr);
833 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
834 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
835 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
836 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
837 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
838 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
839 mei_hdr->length);
Tomas Winkler19838fb2012-11-01 21:17:15 +0200840
841 ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300842 if (ret)
843 goto end;
844
845 } else {
846 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
847 ret = mei_irq_thread_read_client_message(cmpl_list,
848 dev, mei_hdr);
849 if (ret)
850 goto end;
851
852 }
853
854 /* reset the number of slots and header */
855 *slots = mei_count_full_read_slots(dev);
856 dev->rd_msg_hdr = 0;
857
858 if (*slots == -EOVERFLOW) {
859 /* overflow - reset */
860 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
861 /* set the event since message has been read */
862 ret = -ERANGE;
863 goto end;
864 }
865end:
866 return ret;
867}
868
869
870/**
871 * mei_irq_thread_write_handler - bottom half write routine after
872 * ISR to handle the write processing.
873 *
Oren Weilfb7d8792011-05-15 13:43:42 +0300874 * @dev: the device structure
Tomas Winkler9a84d612012-11-18 15:13:18 +0200875 * @cmpl_list: An instance of our list structure
Oren Weilfb7d8792011-05-15 13:43:42 +0300876 *
877 * returns 0 on success, <0 on failure.
878 */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200879static int mei_irq_thread_write_handler(struct mei_device *dev,
880 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300881{
882
883 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200884 struct mei_cl_cb *pos = NULL, *next = NULL;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200885 struct mei_cl_cb *list;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200886 s32 slots;
Oren Weilfb7d8792011-05-15 13:43:42 +0300887 int ret;
888
Tomas Winkler726917f2012-06-25 23:46:28 +0300889 if (!mei_hbuf_is_empty(dev)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300890 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
891 return 0;
892 }
Tomas Winkler9a84d612012-11-18 15:13:18 +0200893 slots = mei_hbuf_empty_slots(dev);
894 if (slots <= 0)
Tomas Winkler7d5e0e52012-06-19 09:13:36 +0300895 return -EMSGSIZE;
896
Oren Weilfb7d8792011-05-15 13:43:42 +0300897 /* complete all waiting for write CB */
898 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
899
900 list = &dev->write_waiting_list;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200901 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200902 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200903 if (cl == NULL)
904 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +0300905
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200906 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200907 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200908 if (MEI_WRITING == cl->writing_state &&
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200909 pos->fop_type == MEI_FOP_WRITE &&
910 cl != &dev->iamthif_cl) {
Tomas Winkler483136e2012-07-04 19:24:54 +0300911 dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200912 cl->writing_state = MEI_WRITE_COMPLETE;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200913 list_add_tail(&pos->list, &cmpl_list->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200914 }
915 if (cl == &dev->iamthif_cl) {
916 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
917 if (dev->iamthif_flow_control_pending) {
Tomas Winkler9a84d612012-11-18 15:13:18 +0200918 ret = mei_amthif_irq_read(dev, &slots);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200919 if (ret)
920 return ret;
921 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300922 }
923 }
924
Tomas Winklerc216fde2012-08-16 19:39:43 +0300925 if (dev->wd_state == MEI_WD_STOPPING) {
926 dev->wd_state = MEI_WD_IDLE;
Oren Weilfb7d8792011-05-15 13:43:42 +0300927 wake_up_interruptible(&dev->wait_stop_wd);
Oren Weilfb7d8792011-05-15 13:43:42 +0300928 }
929
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200930 if (dev->wr_ext_msg.hdr.length) {
931 mei_write_message(dev, &dev->wr_ext_msg.hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200932 dev->wr_ext_msg.data);
Tomas Winkler9a84d612012-11-18 15:13:18 +0200933 slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200934 dev->wr_ext_msg.hdr.length = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300935 }
Tomas Winklerb210d752012-08-07 00:03:56 +0300936 if (dev->dev_state == MEI_DEV_ENABLED) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300937 if (dev->wd_pending &&
Tomas Winkler483136e2012-07-04 19:24:54 +0300938 mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300939 if (mei_wd_send(dev))
940 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
Tomas Winkler483136e2012-07-04 19:24:54 +0300941 else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl))
942 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300943
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300944 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300945
Tomas Winklerc216fde2012-08-16 19:39:43 +0300946 if (dev->wd_state == MEI_WD_RUNNING)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200947 slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300948 else
Tomas Winkler9a84d612012-11-18 15:13:18 +0200949 slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
Oren Weilfb7d8792011-05-15 13:43:42 +0300950 }
951 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300952
953 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +0200954 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200955 list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200956 cl = pos->cl;
Tomas Winklerc8372092011-11-27 21:43:33 +0200957 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200958 list_del(&pos->list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200959 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300960 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200961 switch (pos->fop_type) {
962 case MEI_FOP_CLOSE:
Tomas Winklerc8372092011-11-27 21:43:33 +0200963 /* send disconnect message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200964 ret = _mei_irq_thread_close(dev, &slots, pos,
965 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200966 if (ret)
967 return ret;
968
969 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200970 case MEI_FOP_READ:
Tomas Winklerc8372092011-11-27 21:43:33 +0200971 /* send flow control message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200972 ret = _mei_irq_thread_read(dev, &slots, pos,
973 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200974 if (ret)
975 return ret;
976
977 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200978 case MEI_FOP_IOCTL:
Tomas Winklerc8372092011-11-27 21:43:33 +0200979 /* connect message */
Natalia Ovsyanikove8cd29d2011-12-05 00:16:54 +0200980 if (mei_other_client_is_connecting(dev, cl))
Tomas Winklerc8372092011-11-27 21:43:33 +0200981 continue;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200982 ret = _mei_irq_thread_ioctl(dev, &slots, pos,
983 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200984 if (ret)
985 return ret;
986
987 break;
988
989 default:
990 BUG();
991 }
992
Oren Weilfb7d8792011-05-15 13:43:42 +0300993 }
994 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200995 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200996 list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200997 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200998 if (cl == NULL)
999 continue;
Tomas Winklerbe9d87a2012-11-18 15:13:19 +02001000 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
1001 dev_dbg(&dev->pdev->dev,
1002 "No flow control credentials for client %d, not sending.\n",
1003 cl->host_client_id);
1004 continue;
1005 }
Oren Weilfb7d8792011-05-15 13:43:42 +03001006
Tomas Winklerbe9d87a2012-11-18 15:13:19 +02001007 if (cl == &dev->iamthif_cl)
Tomas Winkler9a84d612012-11-18 15:13:18 +02001008 ret = mei_amthif_irq_write_complete(dev, &slots,
Tomas Winkler24c656e2012-11-18 15:13:17 +02001009 pos, cmpl_list);
Tomas Winklerbe9d87a2012-11-18 15:13:19 +02001010 else
1011 ret = mei_irq_thread_write_complete(dev, &slots, pos,
1012 cmpl_list);
1013 if (ret)
1014 return ret;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001015
Oren Weilfb7d8792011-05-15 13:43:42 +03001016 }
1017 return 0;
1018}
1019
1020
1021
1022/**
1023 * mei_timer - timer function.
1024 *
1025 * @work: pointer to the work_struct structure
1026 *
1027 * NOTE: This function is called by timer interrupt work
1028 */
Oren Weila61c6532011-09-07 09:03:13 +03001029void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +03001030{
1031 unsigned long timeout;
1032 struct mei_cl *cl_pos = NULL;
1033 struct mei_cl *cl_next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001034 struct mei_cl_cb *cb_pos = NULL;
1035 struct mei_cl_cb *cb_next = NULL;
1036
1037 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +03001038 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +03001039
1040
1041 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +03001042 if (dev->dev_state != MEI_DEV_ENABLED) {
1043 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001044 if (dev->init_clients_timer) {
1045 if (--dev->init_clients_timer == 0) {
1046 dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
1047 dev->init_clients_state);
1048 mei_reset(dev, 1);
1049 }
1050 }
1051 }
1052 goto out;
1053 }
1054 /*** connect/disconnect timeouts ***/
1055 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
1056 if (cl_pos->timer_count) {
1057 if (--cl_pos->timer_count == 0) {
1058 dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
1059 mei_reset(dev, 1);
1060 goto out;
1061 }
1062 }
1063 }
1064
Oren Weilfb7d8792011-05-15 13:43:42 +03001065 if (dev->iamthif_stall_timer) {
1066 if (--dev->iamthif_stall_timer == 0) {
Masanari Iida32de21f2012-01-25 23:14:56 +09001067 dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +03001068 mei_reset(dev, 1);
1069 dev->iamthif_msg_buf_size = 0;
1070 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001071 dev->iamthif_canceled = false;
1072 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +03001073 dev->iamthif_state = MEI_IAMTHIF_IDLE;
1074 dev->iamthif_timer = 0;
1075
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001076 mei_io_cb_free(dev->iamthif_current_cb);
1077 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001078
1079 dev->iamthif_file_object = NULL;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001080 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001081 }
1082 }
1083
1084 if (dev->iamthif_timer) {
1085
1086 timeout = dev->iamthif_timer +
Tomas Winkler3870c322012-11-01 21:17:14 +02001087 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilfb7d8792011-05-15 13:43:42 +03001088
1089 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
1090 dev->iamthif_timer);
1091 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
1092 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
1093 if (time_after(jiffies, timeout)) {
1094 /*
1095 * User didn't read the AMTHI data on time (15sec)
1096 * freeing AMTHI for other requests
1097 */
1098
1099 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
1100
Tomas Winklere773efc2012-11-11 17:37:58 +02001101 list_for_each_entry_safe(cb_pos, cb_next,
1102 &dev->amthif_rd_complete_list.list, list) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001103
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001104 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +03001105
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001106 /* Finding the AMTHI entry. */
1107 if (cl_pos == &dev->iamthif_cl)
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001108 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001109 }
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001110 mei_io_cb_free(dev->iamthif_current_cb);
1111 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001112
1113 dev->iamthif_file_object->private_data = NULL;
1114 dev->iamthif_file_object = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001115 dev->iamthif_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001116 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001117
1118 }
1119 }
1120out:
Tomas Winkler441ab502011-12-13 23:39:34 +02001121 schedule_delayed_work(&dev->timer_work, 2 * HZ);
1122 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +03001123}
1124
1125/**
1126 * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
1127 * processing.
1128 *
1129 * @irq: The irq number
1130 * @dev_id: pointer to the device structure
1131 *
1132 * returns irqreturn_t
1133 *
1134 */
1135irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
1136{
1137 struct mei_device *dev = (struct mei_device *) dev_id;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001138 struct mei_cl_cb complete_list;
Oren Weilfb7d8792011-05-15 13:43:42 +03001139 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
1140 struct mei_cl *cl;
1141 s32 slots;
1142 int rets;
1143 bool bus_message_received;
1144
1145
1146 dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
1147 /* initialize our complete list */
1148 mutex_lock(&dev->device_lock);
Tomas Winkler0288c7c2011-06-06 10:44:34 +03001149 mei_io_list_init(&complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001150 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001151
1152 /* Ack the interrupt here
Justin P. Mattock5f9092f32012-03-12 07:18:09 -07001153 * In case of MSI we don't go through the quick handler */
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001154 if (pci_dev_msi_enabled(dev->pdev))
1155 mei_reg_write(dev, H_CSR, dev->host_hw_state);
1156
Oren Weilfb7d8792011-05-15 13:43:42 +03001157 dev->me_hw_state = mei_mecsr_read(dev);
1158
1159 /* check if ME wants a reset */
1160 if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
Tomas Winklerb210d752012-08-07 00:03:56 +03001161 dev->dev_state != MEI_DEV_RESETING &&
1162 dev->dev_state != MEI_DEV_INITIALIZING) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001163 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1164 mei_reset(dev, 1);
1165 mutex_unlock(&dev->device_lock);
1166 return IRQ_HANDLED;
1167 }
1168
1169 /* check if we need to start the dev */
1170 if ((dev->host_hw_state & H_RDY) == 0) {
1171 if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
1172 dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
1173 dev->host_hw_state |= (H_IE | H_IG | H_RDY);
1174 mei_hcsr_set(dev);
Tomas Winklerb210d752012-08-07 00:03:56 +03001175 dev->dev_state = MEI_DEV_INIT_CLIENTS;
Oren Weilfb7d8792011-05-15 13:43:42 +03001176 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
1177 /* link is established
1178 * start sending messages.
1179 */
Tomas Winklerc95efb72011-05-25 17:28:21 +03001180 mei_host_start_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001181 mutex_unlock(&dev->device_lock);
1182 return IRQ_HANDLED;
1183 } else {
1184 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1185 mutex_unlock(&dev->device_lock);
1186 return IRQ_HANDLED;
1187 }
1188 }
Justin P. Mattock5f9092f32012-03-12 07:18:09 -07001189 /* check slots available for reading */
Oren Weilfb7d8792011-05-15 13:43:42 +03001190 slots = mei_count_full_read_slots(dev);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +02001191 while (slots > 0) {
1192 /* we have urgent data to send so break the read */
1193 if (dev->wr_ext_msg.hdr.length)
1194 break;
1195 dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots);
Oren Weilfb7d8792011-05-15 13:43:42 +03001196 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
1197 rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
1198 if (rets)
1199 goto end;
1200 }
Tomas Winkler9a84d612012-11-18 15:13:18 +02001201 rets = mei_irq_thread_write_handler(dev, &complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001202end:
1203 dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
1204 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler726917f2012-06-25 23:46:28 +03001205 dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001206
1207 bus_message_received = false;
1208 if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
1209 dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
1210 bus_message_received = true;
1211 }
1212 mutex_unlock(&dev->device_lock);
1213 if (bus_message_received) {
1214 dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
1215 wake_up_interruptible(&dev->wait_recvd_msg);
1216 bus_message_received = false;
1217 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001218 if (list_empty(&complete_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +03001219 return IRQ_HANDLED;
1220
1221
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001222 list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001223 cl = cb_pos->cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001224 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001225 if (cl) {
1226 if (cl != &dev->iamthif_cl) {
1227 dev_dbg(&dev->pdev->dev, "completing call back.\n");
1228 _mei_cmpl(cl, cb_pos);
1229 cb_pos = NULL;
1230 } else if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +02001231 mei_amthif_complete(dev, cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +03001232 }
1233 }
1234 }
1235 return IRQ_HANDLED;
1236}