aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei/hbm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mei/hbm.c')
-rw-r--r--drivers/misc/mei/hbm.c281
1 files changed, 152 insertions, 129 deletions
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 28cd74c073b9..4960288e543a 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -21,7 +21,41 @@
21 21
22#include "mei_dev.h" 22#include "mei_dev.h"
23#include "hbm.h" 23#include "hbm.h"
24#include "hw-me.h" 24#include "client.h"
25
26static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
27{
28#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
29 switch (status) {
30 MEI_CL_CS(SUCCESS);
31 MEI_CL_CS(NOT_FOUND);
32 MEI_CL_CS(ALREADY_STARTED);
33 MEI_CL_CS(OUT_OF_RESOURCES);
34 MEI_CL_CS(MESSAGE_SMALL);
35 default: return "unknown";
36 }
37#undef MEI_CL_CCS
38}
39
40/**
41 * mei_cl_conn_status_to_errno - convert client connect response
42 * status to error code
43 *
44 * @status: client connect response status
45 *
46 * returns corresponding error code
47 */
48static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
49{
50 switch (status) {
51 case MEI_CL_CONN_SUCCESS: return 0;
52 case MEI_CL_CONN_NOT_FOUND: return -ENOTTY;
53 case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY;
54 case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
55 case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL;
56 default: return -EINVAL;
57 }
58}
25 59
26/** 60/**
27 * mei_hbm_me_cl_allocate - allocates storage for me clients 61 * mei_hbm_me_cl_allocate - allocates storage for me clients
@@ -100,33 +134,6 @@ bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
100 134
101 135
102/** 136/**
103 * is_treat_specially_client - checks if the message belongs
104 * to the file private data.
105 *
106 * @cl: private data of the file object
107 * @rs: connect response bus message
108 *
109 */
110static bool is_treat_specially_client(struct mei_cl *cl,
111 struct hbm_client_connect_response *rs)
112{
113 if (mei_hbm_cl_addr_equal(cl, rs)) {
114 if (!rs->status) {
115 cl->state = MEI_FILE_CONNECTED;
116 cl->status = 0;
117
118 } else {
119 cl->state = MEI_FILE_DISCONNECTED;
120 cl->status = -ENODEV;
121 }
122 cl->timer_count = 0;
123
124 return true;
125 }
126 return false;
127}
128
129/**
130 * mei_hbm_idle - set hbm to idle state 137 * mei_hbm_idle - set hbm to idle state
131 * 138 *
132 * @dev: the device structure 139 * @dev: the device structure
@@ -147,13 +154,13 @@ int mei_hbm_start_wait(struct mei_device *dev)
147 ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, 154 ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
148 dev->hbm_state == MEI_HBM_IDLE || 155 dev->hbm_state == MEI_HBM_IDLE ||
149 dev->hbm_state >= MEI_HBM_STARTED, 156 dev->hbm_state >= MEI_HBM_STARTED,
150 mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); 157 mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
151 mutex_lock(&dev->device_lock); 158 mutex_lock(&dev->device_lock);
152 159
153 if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) { 160 if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) {
154 dev->hbm_state = MEI_HBM_IDLE; 161 dev->hbm_state = MEI_HBM_IDLE;
155 dev_err(&dev->pdev->dev, "waiting for mei start failed\n"); 162 dev_err(&dev->pdev->dev, "waiting for mei start failed\n");
156 return -ETIMEDOUT; 163 return -ETIME;
157 } 164 }
158 return 0; 165 return 0;
159} 166}
@@ -283,17 +290,18 @@ static int mei_hbm_prop_req(struct mei_device *dev)
283} 290}
284 291
285/** 292/**
286 * mei_hbm_stop_req_prepare - prepare stop request message 293 * mei_hbm_stop_req - send stop request message
287 * 294 *
288 * @dev - mei device 295 * @dev - mei device
289 * @mei_hdr - mei message header 296 * @cl: client info
290 * @data - hbm message body buffer 297 *
298 * This function returns -EIO on write failure
291 */ 299 */
292static void mei_hbm_stop_req_prepare(struct mei_device *dev, 300static int mei_hbm_stop_req(struct mei_device *dev)
293 struct mei_msg_hdr *mei_hdr, unsigned char *data)
294{ 301{
302 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
295 struct hbm_host_stop_request *req = 303 struct hbm_host_stop_request *req =
296 (struct hbm_host_stop_request *)data; 304 (struct hbm_host_stop_request *)dev->wr_msg.data;
297 const size_t len = sizeof(struct hbm_host_stop_request); 305 const size_t len = sizeof(struct hbm_host_stop_request);
298 306
299 mei_hbm_hdr(mei_hdr, len); 307 mei_hbm_hdr(mei_hdr, len);
@@ -301,6 +309,8 @@ static void mei_hbm_stop_req_prepare(struct mei_device *dev,
301 memset(req, 0, len); 309 memset(req, 0, len);
302 req->hbm_cmd = HOST_STOP_REQ_CMD; 310 req->hbm_cmd = HOST_STOP_REQ_CMD;
303 req->reason = DRIVER_STOP_REQUEST; 311 req->reason = DRIVER_STOP_REQUEST;
312
313 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
304} 314}
305 315
306/** 316/**
@@ -319,8 +329,7 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
319 mei_hbm_hdr(mei_hdr, len); 329 mei_hbm_hdr(mei_hdr, len);
320 mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len); 330 mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
321 331
322 dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n", 332 cl_dbg(dev, cl, "sending flow control\n");
323 cl->host_client_id, cl->me_client_id);
324 333
325 return mei_write_message(dev, mei_hdr, dev->wr_msg.data); 334 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
326} 335}
@@ -330,27 +339,34 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
330 * 339 *
331 * @dev: the device structure 340 * @dev: the device structure
332 * @flow: flow control. 341 * @flow: flow control.
342 *
343 * return 0 on success, < 0 otherwise
333 */ 344 */
334static void mei_hbm_add_single_flow_creds(struct mei_device *dev, 345static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
335 struct hbm_flow_control *flow) 346 struct hbm_flow_control *flow)
336{ 347{
337 struct mei_me_client *client; 348 struct mei_me_client *me_cl;
338 int i; 349 int id;
339 350
340 for (i = 0; i < dev->me_clients_num; i++) { 351 id = mei_me_cl_by_id(dev, flow->me_addr);
341 client = &dev->me_clients[i]; 352 if (id < 0) {
342 if (client && flow->me_addr == client->client_id) { 353 dev_err(&dev->pdev->dev, "no such me client %d\n",
343 if (client->props.single_recv_buf) { 354 flow->me_addr);
344 client->mei_flow_ctrl_creds++; 355 return id;
345 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
346 flow->me_addr);
347 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
348 client->mei_flow_ctrl_creds);
349 } else {
350 BUG(); /* error in flow control */
351 }
352 }
353 } 356 }
357
358 me_cl = &dev->me_clients[id];
359 if (me_cl->props.single_recv_buf) {
360 me_cl->mei_flow_ctrl_creds++;
361 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
362 flow->me_addr);
363 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
364 me_cl->mei_flow_ctrl_creds);
365 } else {
366 BUG(); /* error in flow control */
367 }
368
369 return 0;
354} 370}
355 371
356/** 372/**
@@ -362,8 +378,7 @@ static void mei_hbm_add_single_flow_creds(struct mei_device *dev,
362static void mei_hbm_cl_flow_control_res(struct mei_device *dev, 378static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
363 struct hbm_flow_control *flow_control) 379 struct hbm_flow_control *flow_control)
364{ 380{
365 struct mei_cl *cl = NULL; 381 struct mei_cl *cl;
366 struct mei_cl *next = NULL;
367 382
368 if (!flow_control->host_addr) { 383 if (!flow_control->host_addr) {
369 /* single receive buffer */ 384 /* single receive buffer */
@@ -372,7 +387,7 @@ static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
372 } 387 }
373 388
374 /* normal connection */ 389 /* normal connection */
375 list_for_each_entry_safe(cl, next, &dev->file_list, link) { 390 list_for_each_entry(cl, &dev->file_list, link) {
376 if (mei_hbm_cl_addr_equal(cl, flow_control)) { 391 if (mei_hbm_cl_addr_equal(cl, flow_control)) {
377 cl->mei_flow_ctrl_creds++; 392 cl->mei_flow_ctrl_creds++;
378 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n", 393 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
@@ -405,6 +420,25 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
405} 420}
406 421
407/** 422/**
423 * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
424 *
425 * @dev: the device structure
426 * @cl: a client to disconnect from
427 *
428 * This function returns -EIO on write failure
429 */
430int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
431{
432 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
433 const size_t len = sizeof(struct hbm_client_connect_response);
434
435 mei_hbm_hdr(mei_hdr, len);
436 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, dev->wr_msg.data, len);
437
438 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
439}
440
441/**
408 * mei_hbm_cl_disconnect_res - disconnect response from ME 442 * mei_hbm_cl_disconnect_res - disconnect response from ME
409 * 443 *
410 * @dev: the device structure 444 * @dev: the device structure
@@ -414,29 +448,23 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev,
414 struct hbm_client_connect_response *rs) 448 struct hbm_client_connect_response *rs)
415{ 449{
416 struct mei_cl *cl; 450 struct mei_cl *cl;
417 struct mei_cl_cb *pos = NULL, *next = NULL; 451 struct mei_cl_cb *cb, *next;
418 452
419 dev_dbg(&dev->pdev->dev, 453 dev_dbg(&dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n",
420 "disconnect_response:\n" 454 rs->me_addr, rs->host_addr, rs->status);
421 "ME Client = %d\n" 455
422 "Host Client = %d\n" 456 list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
423 "Status = %d\n", 457 cl = cb->cl;
424 rs->me_addr, 458
425 rs->host_addr, 459 /* this should not happen */
426 rs->status); 460 if (WARN_ON(!cl)) {
427 461 list_del(&cb->list);
428 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
429 cl = pos->cl;
430
431 if (!cl) {
432 list_del(&pos->list);
433 return; 462 return;
434 } 463 }
435 464
436 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
437 if (mei_hbm_cl_addr_equal(cl, rs)) { 465 if (mei_hbm_cl_addr_equal(cl, rs)) {
438 list_del(&pos->list); 466 list_del(&cb->list);
439 if (!rs->status) 467 if (rs->status == MEI_CL_DISCONN_SUCCESS)
440 cl->state = MEI_FILE_DISCONNECTED; 468 cl->state = MEI_FILE_DISCONNECTED;
441 469
442 cl->status = 0; 470 cl->status = 0;
@@ -476,46 +504,41 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,
476{ 504{
477 505
478 struct mei_cl *cl; 506 struct mei_cl *cl;
479 struct mei_cl_cb *pos = NULL, *next = NULL; 507 struct mei_cl_cb *cb, *next;
480 508
481 dev_dbg(&dev->pdev->dev, 509 dev_dbg(&dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n",
482 "connect_response:\n" 510 rs->me_addr, rs->host_addr,
483 "ME Client = %d\n" 511 mei_cl_conn_status_str(rs->status));
484 "Host Client = %d\n"
485 "Status = %d\n",
486 rs->me_addr,
487 rs->host_addr,
488 rs->status);
489 512
490 /* if WD or iamthif client treat specially */ 513 cl = NULL;
491 514
492 if (is_treat_specially_client(&dev->wd_cl, rs)) { 515 list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
493 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
494 mei_watchdog_register(dev);
495 516
496 return; 517 cl = cb->cl;
497 } 518 /* this should not happen */
519 if (WARN_ON(!cl)) {
520 list_del_init(&cb->list);
521 continue;
522 }
498 523
499 if (is_treat_specially_client(&dev->iamthif_cl, rs)) { 524 if (cb->fop_type != MEI_FOP_CONNECT)
500 dev->iamthif_state = MEI_IAMTHIF_IDLE; 525 continue;
501 return;
502 }
503 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
504 526
505 cl = pos->cl; 527 if (mei_hbm_cl_addr_equal(cl, rs)) {
506 if (!cl) { 528 list_del(&cb->list);
507 list_del(&pos->list); 529 break;
508 return;
509 }
510 if (pos->fop_type == MEI_FOP_IOCTL) {
511 if (is_treat_specially_client(cl, rs)) {
512 list_del(&pos->list);
513 cl->status = 0;
514 cl->timer_count = 0;
515 break;
516 }
517 } 530 }
518 } 531 }
532
533 if (!cl)
534 return;
535
536 cl->timer_count = 0;
537 if (rs->status == MEI_CL_CONN_SUCCESS)
538 cl->state = MEI_FILE_CONNECTED;
539 else
540 cl->state = MEI_FILE_DISCONNECTED;
541 cl->status = mei_cl_conn_status_to_errno(rs->status);
519} 542}
520 543
521 544
@@ -525,32 +548,34 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,
525 * 548 *
526 * @dev: the device structure. 549 * @dev: the device structure.
527 * @disconnect_req: disconnect request bus message from the me 550 * @disconnect_req: disconnect request bus message from the me
551 *
552 * returns -ENOMEM on allocation failure
528 */ 553 */
529static void mei_hbm_fw_disconnect_req(struct mei_device *dev, 554static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
530 struct hbm_client_connect_request *disconnect_req) 555 struct hbm_client_connect_request *disconnect_req)
531{ 556{
532 struct mei_cl *cl, *next; 557 struct mei_cl *cl;
533 const size_t len = sizeof(struct hbm_client_connect_response); 558 struct mei_cl_cb *cb;
534 559
535 list_for_each_entry_safe(cl, next, &dev->file_list, link) { 560 list_for_each_entry(cl, &dev->file_list, link) {
536 if (mei_hbm_cl_addr_equal(cl, disconnect_req)) { 561 if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
537 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n", 562 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
538 disconnect_req->host_addr, 563 disconnect_req->host_addr,
539 disconnect_req->me_addr); 564 disconnect_req->me_addr);
540 cl->state = MEI_FILE_DISCONNECTED; 565 cl->state = MEI_FILE_DISCONNECTED;
541 cl->timer_count = 0; 566 cl->timer_count = 0;
542 if (cl == &dev->wd_cl) 567
543 dev->wd_pending = false; 568 cb = mei_io_cb_init(cl, NULL);
544 else if (cl == &dev->iamthif_cl) 569 if (!cb)
545 dev->iamthif_timer = 0; 570 return -ENOMEM;
546 571 cb->fop_type = MEI_FOP_DISCONNECT_RSP;
547 /* prepare disconnect response */ 572 cl_dbg(dev, cl, "add disconnect response as first\n");
548 mei_hbm_hdr(&dev->wr_ext_msg.hdr, len); 573 list_add(&cb->list, &dev->ctrl_wr_list.list);
549 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, 574
550 dev->wr_ext_msg.data, len);
551 break; 575 break;
552 } 576 }
553 } 577 }
578 return 0;
554} 579}
555 580
556 581
@@ -629,10 +654,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
629 dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n"); 654 dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
630 655
631 dev->hbm_state = MEI_HBM_STOPPED; 656 dev->hbm_state = MEI_HBM_STOPPED;
632 mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr, 657 if (mei_hbm_stop_req(dev)) {
633 dev->wr_msg.data);
634 if (mei_write_message(dev, &dev->wr_msg.hdr,
635 dev->wr_msg.data)) {
636 dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n"); 658 dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
637 return -EIO; 659 return -EIO;
638 } 660 }
@@ -778,10 +800,11 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
778 800
779 case ME_STOP_REQ_CMD: 801 case ME_STOP_REQ_CMD:
780 dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n"); 802 dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
781
782 dev->hbm_state = MEI_HBM_STOPPED; 803 dev->hbm_state = MEI_HBM_STOPPED;
783 mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr, 804 if (mei_hbm_stop_req(dev)) {
784 dev->wr_ext_msg.data); 805 dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
806 return -EIO;
807 }
785 break; 808 break;
786 default: 809 default:
787 BUG(); 810 BUG();