]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/misc/mei/hbm.c
9956aaf58aa4bbc61121acbc403504547b2a1d60
[karo-tx-linux.git] / drivers / misc / mei / hbm.c
1 /*
2  *
3  * Intel Management Engine Interface (Intel MEI) Linux driver
4  * Copyright (c) 2003-2012, Intel Corporation.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  */
16
17 #include <linux/pci.h>
18 #include <linux/sched.h>
19 #include <linux/wait.h>
20 #include <linux/mei.h>
21
22 #include "mei_dev.h"
23 #include "hbm.h"
24 #include "interface.h"
25
26 /**
27  * mei_hbm_cl_hdr - construct client hbm header
28  * @cl: - client
29  * @hbm_cmd: host bus message command
30  * @buf: buffer for cl header
31  * @len: buffer length
32  */
33 static inline
34 void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
35 {
36         struct mei_hbm_cl_cmd *cmd = buf;
37
38         memset(cmd, 0, len);
39
40         cmd->hbm_cmd = hbm_cmd;
41         cmd->host_addr = cl->host_client_id;
42         cmd->me_addr = cl->me_client_id;
43 }
44
45 /**
46  * same_disconn_addr - tells if they have the same address
47  *
48  * @file: private data of the file object.
49  * @disconn: disconnection request.
50  *
51  * returns true if addres are same
52  */
53 static inline
54 bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
55 {
56         struct mei_hbm_cl_cmd *cmd = buf;
57         return cl->host_client_id == cmd->host_addr &&
58                 cl->me_client_id == cmd->me_addr;
59 }
60
61
62 /**
63  * is_treat_specially_client - checks if the message belongs
64  * to the file private data.
65  *
66  * @cl: private data of the file object
67  * @rs: connect response bus message
68  *
69  */
70 static bool is_treat_specially_client(struct mei_cl *cl,
71                 struct hbm_client_connect_response *rs)
72 {
73         if (mei_hbm_cl_addr_equal(cl, rs)) {
74                 if (!rs->status) {
75                         cl->state = MEI_FILE_CONNECTED;
76                         cl->status = 0;
77
78                 } else {
79                         cl->state = MEI_FILE_DISCONNECTED;
80                         cl->status = -ENODEV;
81                 }
82                 cl->timer_count = 0;
83
84                 return true;
85         }
86         return false;
87 }
88
89 /**
90  * mei_hbm_start_req - sends start request message.
91  *
92  * @dev: the device structure
93  */
94 void mei_hbm_start_req(struct mei_device *dev)
95 {
96         struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
97         struct hbm_host_version_request *start_req;
98         const size_t len = sizeof(struct hbm_host_version_request);
99
100         mei_hbm_hdr(mei_hdr, len);
101
102         /* host start message */
103         start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
104         memset(start_req, 0, len);
105         start_req->hbm_cmd = HOST_START_REQ_CMD;
106         start_req->host_version.major_version = HBM_MAJOR_VERSION;
107         start_req->host_version.minor_version = HBM_MINOR_VERSION;
108
109         dev->recvd_msg = false;
110         if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
111                 dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
112                 dev->dev_state = MEI_DEV_RESETING;
113                 mei_reset(dev, 1);
114         }
115         dev->init_clients_state = MEI_START_MESSAGE;
116         dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
117         return ;
118 }
119
120 /**
121  * mei_hbm_enum_clients_req - sends enumeration client request message.
122  *
123  * @dev: the device structure
124  *
125  * returns none.
126  */
127 static void mei_hbm_enum_clients_req(struct mei_device *dev)
128 {
129         struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
130         struct hbm_host_enum_request *enum_req;
131         const size_t len = sizeof(struct hbm_host_enum_request);
132         /* enumerate clients */
133         mei_hbm_hdr(mei_hdr, len);
134
135         enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
136         memset(enum_req, 0, len);
137         enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
138
139         if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
140                 dev->dev_state = MEI_DEV_RESETING;
141                 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
142                 mei_reset(dev, 1);
143         }
144         dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
145         dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
146         return;
147 }
148
149 /**
150  * mei_hbm_prop_requsest - request property for a single client
151  *
152  * @dev: the device structure
153  *
154  * returns none.
155  */
156
157 static int mei_hbm_prop_req(struct mei_device *dev)
158 {
159
160         struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
161         struct hbm_props_request *prop_req;
162         const size_t len = sizeof(struct hbm_props_request);
163         unsigned long next_client_index;
164         u8 client_num;
165
166
167         client_num = dev->me_client_presentation_num;
168
169         next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
170                                           dev->me_client_index);
171
172         /* We got all client properties */
173         if (next_client_index == MEI_CLIENTS_MAX) {
174                 schedule_work(&dev->init_work);
175
176                 return 0;
177         }
178
179         dev->me_clients[client_num].client_id = next_client_index;
180         dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
181
182         mei_hbm_hdr(mei_hdr, len);
183         prop_req = (struct hbm_props_request *)dev->wr_msg.data;
184
185         memset(prop_req, 0, sizeof(struct hbm_props_request));
186
187
188         prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
189         prop_req->address = next_client_index;
190
191         if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
192                 dev->dev_state = MEI_DEV_RESETING;
193                 dev_err(&dev->pdev->dev, "Properties request command failed\n");
194                 mei_reset(dev, 1);
195
196                 return -EIO;
197         }
198
199         dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
200         dev->me_client_index = next_client_index;
201
202         return 0;
203 }
204
205 /**
206  * mei_hbm_stop_req_prepare - perpare stop request message
207  *
208  * @dev - mei device
209  * @mei_hdr - mei message header
210  * @data - hbm message body buffer
211  */
212 static void mei_hbm_stop_req_prepare(struct mei_device *dev,
213                 struct mei_msg_hdr *mei_hdr, unsigned char *data)
214 {
215         struct hbm_host_stop_request *req =
216                         (struct hbm_host_stop_request *)data;
217         const size_t len = sizeof(struct hbm_host_stop_request);
218
219         mei_hbm_hdr(mei_hdr, len);
220
221         memset(req, 0, len);
222         req->hbm_cmd = HOST_STOP_REQ_CMD;
223         req->reason = DRIVER_STOP_REQUEST;
224 }
225
226 /**
227  * mei_hbm_cl_flow_control_req - sends flow control requst.
228  *
229  * @dev: the device structure
230  * @cl: client info
231  *
232  * This function returns -EIO on write failure
233  */
234 int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
235 {
236         struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
237         const size_t len = sizeof(struct hbm_flow_control);
238
239         mei_hbm_hdr(mei_hdr, len);
240         mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
241
242         dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
243                 cl->host_client_id, cl->me_client_id);
244
245         return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
246 }
247
248 /**
249  * add_single_flow_creds - adds single buffer credentials.
250  *
251  * @file: private data ot the file object.
252  * @flow: flow control.
253  */
254 static void mei_hbm_add_single_flow_creds(struct mei_device *dev,
255                                   struct hbm_flow_control *flow)
256 {
257         struct mei_me_client *client;
258         int i;
259
260         for (i = 0; i < dev->me_clients_num; i++) {
261                 client = &dev->me_clients[i];
262                 if (client && flow->me_addr == client->client_id) {
263                         if (client->props.single_recv_buf) {
264                                 client->mei_flow_ctrl_creds++;
265                                 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
266                                     flow->me_addr);
267                                 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
268                                     client->mei_flow_ctrl_creds);
269                         } else {
270                                 BUG();  /* error in flow control */
271                         }
272                 }
273         }
274 }
275
276 /**
277  * mei_hbm_cl_flow_control_res - flow control response from me
278  *
279  * @dev: the device structure
280  * @flow_control: flow control response bus message
281  */
282 static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
283                 struct hbm_flow_control *flow_control)
284 {
285         struct mei_cl *cl = NULL;
286         struct mei_cl *next = NULL;
287
288         if (!flow_control->host_addr) {
289                 /* single receive buffer */
290                 mei_hbm_add_single_flow_creds(dev, flow_control);
291                 return;
292         }
293
294         /* normal connection */
295         list_for_each_entry_safe(cl, next, &dev->file_list, link) {
296                 if (mei_hbm_cl_addr_equal(cl, flow_control)) {
297                         cl->mei_flow_ctrl_creds++;
298                         dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
299                                 flow_control->host_addr, flow_control->me_addr);
300                         dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
301                                     cl->mei_flow_ctrl_creds);
302                                 break;
303                 }
304         }
305 }
306
307
308 /**
309  * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
310  *
311  * @dev: the device structure
312  * @cl: a client to disconnect from
313  *
314  * This function returns -EIO on write failure
315  */
316 int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
317 {
318         struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
319         const size_t len = sizeof(struct hbm_client_connect_request);
320
321         mei_hbm_hdr(mei_hdr, len);
322         mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len);
323
324         return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
325 }
326
327 /**
328  * mei_hbm_cl_disconnect_res - disconnect response from ME
329  *
330  * @dev: the device structure
331  * @rs: disconnect response bus message
332  */
333 static void mei_hbm_cl_disconnect_res(struct mei_device *dev,
334                 struct hbm_client_connect_response *rs)
335 {
336         struct mei_cl *cl;
337         struct mei_cl_cb *pos = NULL, *next = NULL;
338
339         dev_dbg(&dev->pdev->dev,
340                         "disconnect_response:\n"
341                         "ME Client = %d\n"
342                         "Host Client = %d\n"
343                         "Status = %d\n",
344                         rs->me_addr,
345                         rs->host_addr,
346                         rs->status);
347
348         list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
349                 cl = pos->cl;
350
351                 if (!cl) {
352                         list_del(&pos->list);
353                         return;
354                 }
355
356                 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
357                 if (mei_hbm_cl_addr_equal(cl, rs)) {
358                         list_del(&pos->list);
359                         if (!rs->status)
360                                 cl->state = MEI_FILE_DISCONNECTED;
361
362                         cl->status = 0;
363                         cl->timer_count = 0;
364                         break;
365                 }
366         }
367 }
368
369 /**
370  * mei_hbm_cl_connect_req - send connection request to specific me client
371  *
372  * @dev: the device structure
373  * @cl: a client to connect to
374  *
375  * returns -EIO on write failure
376  */
377 int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
378 {
379         struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
380         const size_t len = sizeof(struct hbm_client_connect_request);
381
382         mei_hbm_hdr(mei_hdr, len);
383         mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len);
384
385         return mei_write_message(dev, mei_hdr,  dev->wr_msg.data);
386 }
387
388 /**
389  * mei_hbm_cl_connect_res - connect resposne from the ME
390  *
391  * @dev: the device structure
392  * @rs: connect response bus message
393  */
394 static void mei_hbm_cl_connect_res(struct mei_device *dev,
395                 struct hbm_client_connect_response *rs)
396 {
397
398         struct mei_cl *cl;
399         struct mei_cl_cb *pos = NULL, *next = NULL;
400
401         dev_dbg(&dev->pdev->dev,
402                         "connect_response:\n"
403                         "ME Client = %d\n"
404                         "Host Client = %d\n"
405                         "Status = %d\n",
406                         rs->me_addr,
407                         rs->host_addr,
408                         rs->status);
409
410         /* if WD or iamthif client treat specially */
411
412         if (is_treat_specially_client(&dev->wd_cl, rs)) {
413                 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
414                 mei_watchdog_register(dev);
415
416                 return;
417         }
418
419         if (is_treat_specially_client(&dev->iamthif_cl, rs)) {
420                 dev->iamthif_state = MEI_IAMTHIF_IDLE;
421                 return;
422         }
423         list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
424
425                 cl = pos->cl;
426                 if (!cl) {
427                         list_del(&pos->list);
428                         return;
429                 }
430                 if (pos->fop_type == MEI_FOP_IOCTL) {
431                         if (is_treat_specially_client(cl, rs)) {
432                                 list_del(&pos->list);
433                                 cl->status = 0;
434                                 cl->timer_count = 0;
435                                 break;
436                         }
437                 }
438         }
439 }
440
441
442 /**
443  * mei_client_disconnect_request - disconnect request initiated by me
444  *  host sends disoconnect response
445  *
446  * @dev: the device structure.
447  * @disconnect_req: disconnect request bus message from the me
448  */
449 static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
450                 struct hbm_client_connect_request *disconnect_req)
451 {
452         struct mei_cl *cl, *next;
453         const size_t len = sizeof(struct hbm_client_connect_response);
454
455         list_for_each_entry_safe(cl, next, &dev->file_list, link) {
456                 if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
457                         dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
458                                         disconnect_req->host_addr,
459                                         disconnect_req->me_addr);
460                         cl->state = MEI_FILE_DISCONNECTED;
461                         cl->timer_count = 0;
462                         if (cl == &dev->wd_cl)
463                                 dev->wd_pending = false;
464                         else if (cl == &dev->iamthif_cl)
465                                 dev->iamthif_timer = 0;
466
467                         /* prepare disconnect response */
468                         mei_hbm_hdr(&dev->wr_ext_msg.hdr, len);
469                         mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD,
470                                          dev->wr_ext_msg.data, len);
471                         break;
472                 }
473         }
474 }
475
476
477 /**
478  * mei_hbm_dispatch - bottom half read routine after ISR to
479  * handle the read bus message cmd processing.
480  *
481  * @dev: the device structure
482  * @mei_hdr: header of bus message
483  */
484 void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
485 {
486         struct mei_bus_message *mei_msg;
487         struct mei_me_client *me_client;
488         struct hbm_host_version_response *version_res;
489         struct hbm_client_connect_response *connect_res;
490         struct hbm_client_connect_response *disconnect_res;
491         struct hbm_client_connect_request *disconnect_req;
492         struct hbm_flow_control *flow_control;
493         struct hbm_props_response *props_res;
494         struct hbm_host_enum_response *enum_res;
495
496         /* read the message to our buffer */
497         BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
498         mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
499         mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
500
501         switch (mei_msg->hbm_cmd) {
502         case HOST_START_RES_CMD:
503                 version_res = (struct hbm_host_version_response *)mei_msg;
504                 if (!version_res->host_version_supported) {
505                         dev->version = version_res->me_max_version;
506                         dev_dbg(&dev->pdev->dev, "version mismatch.\n");
507
508                         mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
509                                                 dev->wr_msg.data);
510                         mei_write_message(dev, &dev->wr_msg.hdr,
511                                         dev->wr_msg.data);
512                         return;
513                 }
514
515                 dev->version.major_version = HBM_MAJOR_VERSION;
516                 dev->version.minor_version = HBM_MINOR_VERSION;
517                 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
518                     dev->init_clients_state == MEI_START_MESSAGE) {
519                         dev->init_clients_timer = 0;
520                         mei_hbm_enum_clients_req(dev);
521                 } else {
522                         dev->recvd_msg = false;
523                         dev_dbg(&dev->pdev->dev, "reset due to received hbm: host start\n");
524                         mei_reset(dev, 1);
525                         return;
526                 }
527
528                 dev->recvd_msg = true;
529                 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
530                 break;
531
532         case CLIENT_CONNECT_RES_CMD:
533                 connect_res = (struct hbm_client_connect_response *) mei_msg;
534                 mei_hbm_cl_connect_res(dev, connect_res);
535                 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
536                 wake_up(&dev->wait_recvd_msg);
537                 break;
538
539         case CLIENT_DISCONNECT_RES_CMD:
540                 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
541                 mei_hbm_cl_disconnect_res(dev, disconnect_res);
542                 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
543                 wake_up(&dev->wait_recvd_msg);
544                 break;
545
546         case MEI_FLOW_CONTROL_CMD:
547                 flow_control = (struct hbm_flow_control *) mei_msg;
548                 mei_hbm_cl_flow_control_res(dev, flow_control);
549                 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
550                 break;
551
552         case HOST_CLIENT_PROPERTIES_RES_CMD:
553                 props_res = (struct hbm_props_response *)mei_msg;
554                 me_client = &dev->me_clients[dev->me_client_presentation_num];
555
556                 if (props_res->status || !dev->me_clients) {
557                         dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
558                         mei_reset(dev, 1);
559                         return;
560                 }
561
562                 if (me_client->client_id != props_res->address) {
563                         dev_err(&dev->pdev->dev,
564                                 "Host client properties reply mismatch\n");
565                         mei_reset(dev, 1);
566
567                         return;
568                 }
569
570                 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
571                     dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
572                         dev_err(&dev->pdev->dev,
573                                 "Unexpected client properties reply\n");
574                         mei_reset(dev, 1);
575
576                         return;
577                 }
578
579                 me_client->props = props_res->client_properties;
580                 dev->me_client_index++;
581                 dev->me_client_presentation_num++;
582
583                 /* request property for the next client */
584                 mei_hbm_prop_req(dev);
585
586                 break;
587
588         case HOST_ENUM_RES_CMD:
589                 enum_res = (struct hbm_host_enum_response *) mei_msg;
590                 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
591                 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
592                     dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
593                                 dev->init_clients_timer = 0;
594                                 dev->me_client_presentation_num = 0;
595                                 dev->me_client_index = 0;
596                                 mei_allocate_me_clients_storage(dev);
597                                 dev->init_clients_state =
598                                         MEI_CLIENT_PROPERTIES_MESSAGE;
599
600                                 /* first property reqeust */
601                                 mei_hbm_prop_req(dev);
602                 } else {
603                         dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
604                         mei_reset(dev, 1);
605                         return;
606                 }
607                 break;
608
609         case HOST_STOP_RES_CMD:
610                 dev->dev_state = MEI_DEV_DISABLED;
611                 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
612                 mei_reset(dev, 1);
613                 break;
614
615         case CLIENT_DISCONNECT_REQ_CMD:
616                 /* search for client */
617                 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
618                 mei_hbm_fw_disconnect_req(dev, disconnect_req);
619                 break;
620
621         case ME_STOP_REQ_CMD:
622
623                 mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
624                                         dev->wr_ext_msg.data);
625                 break;
626         default:
627                 BUG();
628                 break;
629
630         }
631 }
632