mei: pg: fix cat and paste error in comments
[deliverable/linux.git] / drivers / misc / mei / hbm.c
CommitLineData
bb1b0133
TW
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
4fcbc99b 17#include <linux/export.h>
bb1b0133
TW
18#include <linux/pci.h>
19#include <linux/sched.h>
20#include <linux/wait.h>
21#include <linux/mei.h>
180ea05b 22#include <linux/pm_runtime.h>
bb1b0133
TW
23
24#include "mei_dev.h"
0edb23fc 25#include "hbm.h"
12d00665 26#include "client.h"
bb1b0133 27
89778d6e
TW
28static const char *mei_hbm_status_str(enum mei_hbm_status status)
29{
30#define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status
31 switch (status) {
32 MEI_HBM_STATUS(SUCCESS);
33 MEI_HBM_STATUS(CLIENT_NOT_FOUND);
34 MEI_HBM_STATUS(ALREADY_EXISTS);
35 MEI_HBM_STATUS(REJECTED);
36 MEI_HBM_STATUS(INVALID_PARAMETER);
37 MEI_HBM_STATUS(NOT_ALLOWED);
38 MEI_HBM_STATUS(ALREADY_STARTED);
39 MEI_HBM_STATUS(NOT_STARTED);
40 default: return "unknown";
41 }
42#undef MEI_HBM_STATUS
43};
44
285e2996
AU
45static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
46{
47#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
48 switch (status) {
49 MEI_CL_CS(SUCCESS);
50 MEI_CL_CS(NOT_FOUND);
51 MEI_CL_CS(ALREADY_STARTED);
52 MEI_CL_CS(OUT_OF_RESOURCES);
53 MEI_CL_CS(MESSAGE_SMALL);
54 default: return "unknown";
55 }
56#undef MEI_CL_CCS
57}
58
1beeb4b9
AU
59const char *mei_hbm_state_str(enum mei_hbm_state state)
60{
61#define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state
62 switch (state) {
63 MEI_HBM_STATE(IDLE);
64 MEI_HBM_STATE(STARTING);
65 MEI_HBM_STATE(STARTED);
66 MEI_HBM_STATE(ENUM_CLIENTS);
67 MEI_HBM_STATE(CLIENT_PROPERTIES);
68 MEI_HBM_STATE(STOPPED);
69 default:
70 return "unknown";
71 }
72#undef MEI_HBM_STATE
73}
74
285e2996
AU
75/**
76 * mei_cl_conn_status_to_errno - convert client connect response
77 * status to error code
78 *
79 * @status: client connect response status
80 *
81 * returns corresponding error code
82 */
83static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
84{
85 switch (status) {
86 case MEI_CL_CONN_SUCCESS: return 0;
87 case MEI_CL_CONN_NOT_FOUND: return -ENOTTY;
88 case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY;
89 case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
90 case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL;
91 default: return -EINVAL;
92 }
93}
94
84b3294a
TW
95/**
96 * mei_hbm_idle - set hbm to idle state
97 *
98 * @dev: the device structure
99 */
100void mei_hbm_idle(struct mei_device *dev)
101{
102 dev->init_clients_timer = 0;
103 dev->hbm_state = MEI_HBM_IDLE;
104}
105
106/**
25ca6472 107 * mei_me_cl_remove_all - remove all me clients
84b3294a
TW
108 *
109 * @dev: the device structure
110 */
25ca6472 111static void mei_me_cl_remove_all(struct mei_device *dev)
84b3294a 112{
5ca2d388 113 struct mei_me_client *me_cl, *next;
25ca6472
TW
114 list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) {
115 list_del(&me_cl->list);
116 kfree(me_cl);
117 }
118}
5ca2d388 119
25ca6472
TW
120/**
121 * mei_hbm_reset - reset hbm counters and book keeping data structurs
122 *
123 * @dev: the device structure
124 */
125void mei_hbm_reset(struct mei_device *dev)
126{
84b3294a
TW
127 dev->me_client_presentation_num = 0;
128 dev->me_client_index = 0;
129
25ca6472 130 mei_me_cl_remove_all(dev);
84b3294a
TW
131
132 mei_hbm_idle(dev);
133}
134
cd51ed64
TW
135/**
136 * mei_hbm_cl_hdr - construct client hbm header
393b148f 137 *
68d1aa65 138 * @cl: client
cd51ed64
TW
139 * @hbm_cmd: host bus message command
140 * @buf: buffer for cl header
141 * @len: buffer length
142 */
143static inline
144void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
145{
146 struct mei_hbm_cl_cmd *cmd = buf;
147
148 memset(cmd, 0, len);
149
150 cmd->hbm_cmd = hbm_cmd;
151 cmd->host_addr = cl->host_client_id;
152 cmd->me_addr = cl->me_client_id;
153}
154
68d1aa65
TW
155/**
156 * mei_hbm_cl_write - write simple hbm client message
157 *
158 * @dev: the device structure
159 * @cl: client
160 * @hbm_cmd: host bus message command
161 * @len: buffer length
162 */
163static inline
164int mei_hbm_cl_write(struct mei_device *dev,
165 struct mei_cl *cl, u8 hbm_cmd, size_t len)
166{
167 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
168
169 mei_hbm_hdr(mei_hdr, len);
170 mei_hbm_cl_hdr(cl, hbm_cmd, dev->wr_msg.data, len);
171
172 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
173}
174
cd51ed64 175/**
2af89db1
TW
176 * mei_hbm_cl_addr_equal - check if the client's and
177 * the message address match
cd51ed64 178 *
2af89db1
TW
179 * @cl: client
180 * @cmd: hbm client message
cd51ed64 181 *
83ce0741 182 * returns true if addresses are the same
cd51ed64
TW
183 */
184static inline
2af89db1 185bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
cd51ed64 186{
cd51ed64
TW
187 return cl->host_client_id == cmd->host_addr &&
188 cl->me_client_id == cmd->me_addr;
189}
190
2af89db1
TW
191/**
192 * mei_hbm_cl_find_by_cmd - find recipient client
193 *
194 * @dev: the device structure
195 * @buf: a buffer with hbm cl command
196 *
197 * returns the recipient client or NULL if not found
198 */
199static inline
200struct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf)
201{
202 struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf;
203 struct mei_cl *cl;
204
205 list_for_each_entry(cl, &dev->file_list, link)
206 if (mei_hbm_cl_addr_equal(cl, cmd))
207 return cl;
208 return NULL;
209}
210
211
cb02efc3
AU
212/**
213 * mei_hbm_start_wait - wait for start response message.
214 *
215 * @dev: the device structure
216 *
217 * returns 0 on success and < 0 on failure
218 */
9b0d5efc
TW
219int mei_hbm_start_wait(struct mei_device *dev)
220{
221 int ret;
cb02efc3
AU
222
223 if (dev->hbm_state > MEI_HBM_STARTING)
9b0d5efc
TW
224 return 0;
225
226 mutex_unlock(&dev->device_lock);
cb02efc3
AU
227 ret = wait_event_timeout(dev->wait_hbm_start,
228 dev->hbm_state != MEI_HBM_STARTING,
7d93e58d 229 mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
9b0d5efc
TW
230 mutex_lock(&dev->device_lock);
231
cb02efc3 232 if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) {
9b0d5efc 233 dev->hbm_state = MEI_HBM_IDLE;
8b513d0c 234 dev_err(&dev->pdev->dev, "waiting for mei start failed\n");
7ca96aa2 235 return -ETIME;
9b0d5efc
TW
236 }
237 return 0;
238}
239
bb1b0133 240/**
8120e720 241 * mei_hbm_start_req - sends start request message.
bb1b0133
TW
242 *
243 * @dev: the device structure
544f9460
TW
244 *
245 * returns 0 on success and < 0 on failure
bb1b0133 246 */
9b0d5efc 247int mei_hbm_start_req(struct mei_device *dev)
bb1b0133 248{
e46f1874 249 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
bb1b0133
TW
250 struct hbm_host_version_request *start_req;
251 const size_t len = sizeof(struct hbm_host_version_request);
544f9460 252 int ret;
bb1b0133 253
5ca2d388
TW
254 mei_hbm_reset(dev);
255
e46f1874 256 mei_hbm_hdr(mei_hdr, len);
bb1b0133
TW
257
258 /* host start message */
e46f1874 259 start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
bb1b0133
TW
260 memset(start_req, 0, len);
261 start_req->hbm_cmd = HOST_START_REQ_CMD;
262 start_req->host_version.major_version = HBM_MAJOR_VERSION;
263 start_req->host_version.minor_version = HBM_MINOR_VERSION;
264
9b0d5efc 265 dev->hbm_state = MEI_HBM_IDLE;
544f9460
TW
266 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
267 if (ret) {
268 dev_err(&dev->pdev->dev, "version message write failed: ret = %d\n",
269 ret);
270 return ret;
bb1b0133 271 }
544f9460 272
cb02efc3 273 dev->hbm_state = MEI_HBM_STARTING;
bb1b0133 274 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
9b0d5efc 275 return 0;
bb1b0133
TW
276}
277
9b0d5efc 278/*
8120e720 279 * mei_hbm_enum_clients_req - sends enumeration client request message.
bb1b0133
TW
280 *
281 * @dev: the device structure
282 *
544f9460 283 * returns 0 on success and < 0 on failure
bb1b0133 284 */
544f9460 285static int mei_hbm_enum_clients_req(struct mei_device *dev)
bb1b0133 286{
e46f1874 287 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
bb1b0133
TW
288 struct hbm_host_enum_request *enum_req;
289 const size_t len = sizeof(struct hbm_host_enum_request);
544f9460
TW
290 int ret;
291
bb1b0133 292 /* enumerate clients */
e46f1874 293 mei_hbm_hdr(mei_hdr, len);
bb1b0133 294
e46f1874
TW
295 enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
296 memset(enum_req, 0, len);
bb1b0133
TW
297 enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
298
544f9460
TW
299 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
300 if (ret) {
301 dev_err(&dev->pdev->dev, "enumeration request write failed: ret = %d.\n",
302 ret);
303 return ret;
bb1b0133 304 }
9b0d5efc 305 dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
bb1b0133 306 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
544f9460 307 return 0;
bb1b0133
TW
308}
309
5ca2d388
TW
310/*
311 * mei_hbm_me_cl_add - add new me client to the list
312 *
313 * @dev: the device structure
314 * @res: hbm property response
315 *
316 * returns 0 on success and -ENOMEM on allocation failure
317 */
318
319static int mei_hbm_me_cl_add(struct mei_device *dev,
320 struct hbm_props_response *res)
321{
322 struct mei_me_client *me_cl;
323
324 me_cl = kzalloc(sizeof(struct mei_me_client), GFP_KERNEL);
325 if (!me_cl)
326 return -ENOMEM;
327
328 me_cl->props = res->client_properties;
329 me_cl->client_id = res->me_addr;
330 me_cl->mei_flow_ctrl_creds = 0;
331
332 list_add(&me_cl->list, &dev->me_clients);
333 return 0;
334}
335
8120e720 336/**
393b148f 337 * mei_hbm_prop_req - request property for a single client
8120e720
TW
338 *
339 * @dev: the device structure
340 *
544f9460 341 * returns 0 on success and < 0 on failure
8120e720 342 */
bb1b0133 343
8120e720 344static int mei_hbm_prop_req(struct mei_device *dev)
bb1b0133
TW
345{
346
e46f1874 347 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
bb1b0133
TW
348 struct hbm_props_request *prop_req;
349 const size_t len = sizeof(struct hbm_props_request);
350 unsigned long next_client_index;
544f9460 351 int ret;
bb1b0133 352
bb1b0133
TW
353 next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
354 dev->me_client_index);
355
356 /* We got all client properties */
357 if (next_client_index == MEI_CLIENTS_MAX) {
9b0d5efc 358 dev->hbm_state = MEI_HBM_STARTED;
bb1b0133
TW
359 schedule_work(&dev->init_work);
360
361 return 0;
362 }
363
e46f1874
TW
364 mei_hbm_hdr(mei_hdr, len);
365 prop_req = (struct hbm_props_request *)dev->wr_msg.data;
bb1b0133
TW
366
367 memset(prop_req, 0, sizeof(struct hbm_props_request));
368
bb1b0133 369 prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
3438c1f3 370 prop_req->me_addr = next_client_index;
bb1b0133 371
544f9460
TW
372 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
373 if (ret) {
374 dev_err(&dev->pdev->dev, "properties request write failed: ret = %d\n",
375 ret);
376 return ret;
bb1b0133
TW
377 }
378
379 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
380 dev->me_client_index = next_client_index;
381
382 return 0;
383}
384
4fcbc99b
TW
385/*
386 * mei_hbm_pg - sends pg command
387 *
388 * @dev: the device structure
389 * @pg_cmd: the pg command code
390 *
bae1cc7d
TW
391 * returns -EIO on write failure
392 * -EOPNOTSUPP if the operation is not supported by the protocol
4fcbc99b
TW
393 */
394int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
395{
396 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
397 struct hbm_power_gate *req;
398 const size_t len = sizeof(struct hbm_power_gate);
399 int ret;
400
bae1cc7d
TW
401 if (!dev->hbm_f_pg_supported)
402 return -EOPNOTSUPP;
403
4fcbc99b
TW
404 mei_hbm_hdr(mei_hdr, len);
405
406 req = (struct hbm_power_gate *)dev->wr_msg.data;
407 memset(req, 0, len);
408 req->hbm_cmd = pg_cmd;
409
410 ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
411 if (ret)
412 dev_err(&dev->pdev->dev, "power gate command write failed.\n");
413 return ret;
414}
415EXPORT_SYMBOL_GPL(mei_hbm_pg);
416
e46f1874 417/**
6bb948c9 418 * mei_hbm_stop_req - send stop request message
e46f1874
TW
419 *
420 * @dev - mei device
6bb948c9
TW
421 * @cl: client info
422 *
423 * This function returns -EIO on write failure
e46f1874 424 */
6bb948c9 425static int mei_hbm_stop_req(struct mei_device *dev)
e46f1874 426{
6bb948c9 427 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
e46f1874 428 struct hbm_host_stop_request *req =
6bb948c9 429 (struct hbm_host_stop_request *)dev->wr_msg.data;
e46f1874
TW
430 const size_t len = sizeof(struct hbm_host_stop_request);
431
432 mei_hbm_hdr(mei_hdr, len);
433
434 memset(req, 0, len);
435 req->hbm_cmd = HOST_STOP_REQ_CMD;
436 req->reason = DRIVER_STOP_REQUEST;
6bb948c9
TW
437
438 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
e46f1874
TW
439}
440
bb1b0133 441/**
83ce0741 442 * mei_hbm_cl_flow_control_req - sends flow control request.
bb1b0133
TW
443 *
444 * @dev: the device structure
8120e720 445 * @cl: client info
bb1b0133
TW
446 *
447 * This function returns -EIO on write failure
448 */
8120e720 449int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
bb1b0133 450{
bb1b0133 451 const size_t len = sizeof(struct hbm_flow_control);
46922186 452 cl_dbg(dev, cl, "sending flow control\n");
68d1aa65 453 return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, len);
bb1b0133
TW
454}
455
6bbda15f 456/**
393b148f 457 * mei_hbm_add_single_flow_creds - adds single buffer credentials.
6bbda15f 458 *
393b148f 459 * @dev: the device structure
6bbda15f 460 * @flow: flow control.
12d00665
AU
461 *
462 * return 0 on success, < 0 otherwise
6bbda15f 463 */
12d00665 464static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
6bbda15f
TW
465 struct hbm_flow_control *flow)
466{
12d00665 467 struct mei_me_client *me_cl;
12d00665 468
d320832f
TW
469 me_cl = mei_me_cl_by_id(dev, flow->me_addr);
470 if (!me_cl) {
12d00665
AU
471 dev_err(&dev->pdev->dev, "no such me client %d\n",
472 flow->me_addr);
d320832f 473 return -ENOENT;
12d00665
AU
474 }
475
d320832f
TW
476 if (WARN_ON(me_cl->props.single_recv_buf == 0))
477 return -EINVAL;
478
479 me_cl->mei_flow_ctrl_creds++;
480 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
481 flow->me_addr, me_cl->mei_flow_ctrl_creds);
12d00665
AU
482
483 return 0;
6bbda15f
TW
484}
485
486/**
487 * mei_hbm_cl_flow_control_res - flow control response from me
488 *
489 * @dev: the device structure
490 * @flow_control: flow control response bus message
491 */
492static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
2af89db1 493 struct hbm_flow_control *flow_control)
6bbda15f 494{
31f88f57 495 struct mei_cl *cl;
6bbda15f
TW
496
497 if (!flow_control->host_addr) {
498 /* single receive buffer */
499 mei_hbm_add_single_flow_creds(dev, flow_control);
500 return;
501 }
502
2af89db1
TW
503 cl = mei_hbm_cl_find_by_cmd(dev, flow_control);
504 if (cl) {
505 cl->mei_flow_ctrl_creds++;
506 cl_dbg(dev, cl, "flow control creds = %d.\n",
5ca2d388 507 cl->mei_flow_ctrl_creds);
6bbda15f
TW
508 }
509}
510
511
bb1b0133 512/**
8120e720 513 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
bb1b0133
TW
514 *
515 * @dev: the device structure
8120e720 516 * @cl: a client to disconnect from
bb1b0133
TW
517 *
518 * This function returns -EIO on write failure
519 */
8120e720 520int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
bb1b0133 521{
bb1b0133 522 const size_t len = sizeof(struct hbm_client_connect_request);
68d1aa65 523 return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, len);
bb1b0133
TW
524}
525
6bb948c9
TW
526/**
527 * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
528 *
529 * @dev: the device structure
530 * @cl: a client to disconnect from
531 *
532 * This function returns -EIO on write failure
533 */
534int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
535{
6bb948c9 536 const size_t len = sizeof(struct hbm_client_connect_response);
68d1aa65 537 return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, len);
6bb948c9
TW
538}
539
6bbda15f 540/**
12f45ed4
TW
541 * mei_hbm_cl_disconnect_res - update the client state according
542 * disconnect response
6bbda15f 543 *
12f45ed4
TW
544 * @cl: mei host client
545 * @cmd: disconnect client response host bus message
6bbda15f 546 */
12f45ed4
TW
547static void mei_hbm_cl_disconnect_res(struct mei_cl *cl,
548 struct mei_hbm_cl_cmd *cmd)
6bbda15f 549{
12f45ed4
TW
550 struct hbm_client_connect_response *rs =
551 (struct hbm_client_connect_response *)cmd;
6bbda15f 552
12f45ed4 553 dev_dbg(&cl->dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n",
285e2996 554 rs->me_addr, rs->host_addr, rs->status);
6bbda15f 555
12f45ed4
TW
556 if (rs->status == MEI_CL_DISCONN_SUCCESS)
557 cl->state = MEI_FILE_DISCONNECTED;
558 cl->status = 0;
6bbda15f
TW
559}
560
bb1b0133 561/**
8120e720 562 * mei_hbm_cl_connect_req - send connection request to specific me client
bb1b0133
TW
563 *
564 * @dev: the device structure
8120e720 565 * @cl: a client to connect to
bb1b0133 566 *
8120e720 567 * returns -EIO on write failure
bb1b0133 568 */
8120e720 569int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
bb1b0133 570{
bb1b0133 571 const size_t len = sizeof(struct hbm_client_connect_request);
68d1aa65 572 return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, len);
bb1b0133
TW
573}
574
6bbda15f 575/**
12f45ed4
TW
576 * mei_hbm_cl_connect_res - update the client state according
577 * connection response
6bbda15f 578 *
12f45ed4
TW
579 * @cl: mei host client
580 * @cmd: connect client response host bus message
6bbda15f 581 */
12f45ed4
TW
582static void mei_hbm_cl_connect_res(struct mei_cl *cl,
583 struct mei_hbm_cl_cmd *cmd)
6bbda15f 584{
12f45ed4
TW
585 struct hbm_client_connect_response *rs =
586 (struct hbm_client_connect_response *)cmd;
6bbda15f 587
12f45ed4 588 dev_dbg(&cl->dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n",
285e2996
AU
589 rs->me_addr, rs->host_addr,
590 mei_cl_conn_status_str(rs->status));
6bbda15f 591
12f45ed4
TW
592 if (rs->status == MEI_CL_CONN_SUCCESS)
593 cl->state = MEI_FILE_CONNECTED;
594 else
595 cl->state = MEI_FILE_DISCONNECTED;
596 cl->status = mei_cl_conn_status_to_errno(rs->status);
597}
598
599/**
600 * mei_hbm_cl_res - process hbm response received on behalf
601 * an client
602 *
603 * @dev: the device structure
604 * @rs: hbm client message
605 * @fop_type: file operation type
606 */
607static void mei_hbm_cl_res(struct mei_device *dev,
608 struct mei_hbm_cl_cmd *rs,
609 enum mei_cb_file_ops fop_type)
610{
611 struct mei_cl *cl;
612 struct mei_cl_cb *cb, *next;
6bbda15f 613
12f45ed4 614 cl = NULL;
64092858 615 list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
6bbda15f 616
64092858
TW
617 cl = cb->cl;
618 /* this should not happen */
619 if (WARN_ON(!cl)) {
620 list_del_init(&cb->list);
621 continue;
622 }
6bbda15f 623
12f45ed4 624 if (cb->fop_type != fop_type)
64092858 625 continue;
6bbda15f 626
64092858
TW
627 if (mei_hbm_cl_addr_equal(cl, rs)) {
628 list_del(&cb->list);
629 break;
6bbda15f
TW
630 }
631 }
64092858
TW
632
633 if (!cl)
634 return;
635
12f45ed4
TW
636 switch (fop_type) {
637 case MEI_FOP_CONNECT:
638 mei_hbm_cl_connect_res(cl, rs);
639 break;
640 case MEI_FOP_DISCONNECT:
641 mei_hbm_cl_disconnect_res(cl, rs);
642 break;
643 default:
644 return;
645 }
646
64092858 647 cl->timer_count = 0;
12f45ed4 648 wake_up(&cl->wait);
6bbda15f
TW
649}
650
651
bb1b0133 652/**
83ce0741
AU
653 * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
654 * host sends disconnect response
bb1b0133
TW
655 *
656 * @dev: the device structure.
8120e720 657 * @disconnect_req: disconnect request bus message from the me
6bb948c9
TW
658 *
659 * returns -ENOMEM on allocation failure
bb1b0133 660 */
6bb948c9 661static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
bb1b0133
TW
662 struct hbm_client_connect_request *disconnect_req)
663{
31f88f57 664 struct mei_cl *cl;
6bb948c9 665 struct mei_cl_cb *cb;
bb1b0133 666
2af89db1
TW
667 cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
668 if (cl) {
669 cl_dbg(dev, cl, "disconnect request received\n");
670 cl->state = MEI_FILE_DISCONNECTED;
671 cl->timer_count = 0;
672
673 cb = mei_io_cb_init(cl, NULL);
674 if (!cb)
675 return -ENOMEM;
676 cb->fop_type = MEI_FOP_DISCONNECT_RSP;
677 cl_dbg(dev, cl, "add disconnect response as first\n");
678 list_add(&cb->list, &dev->ctrl_wr_list.list);
bb1b0133 679 }
6bb948c9 680 return 0;
bb1b0133
TW
681}
682
bae1cc7d
TW
683/**
684 * mei_hbm_config_features: check what hbm features and commands
685 * are supported by the fw
686 *
687 * @dev: the device structure
688 */
689static void mei_hbm_config_features(struct mei_device *dev)
690{
691 /* Power Gating Isolation Support */
692 dev->hbm_f_pg_supported = 0;
693 if (dev->version.major_version > HBM_MAJOR_VERSION_PGI)
694 dev->hbm_f_pg_supported = 1;
695
696 if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
697 dev->version.minor_version >= HBM_MINOR_VERSION_PGI)
698 dev->hbm_f_pg_supported = 1;
699}
bb1b0133 700
2c9b48ac
TW
701/**
702 * mei_hbm_version_is_supported - checks whether the driver can
703 * support the hbm version of the device
704 *
705 * @dev: the device structure
706 * returns true if driver can support hbm version of the device
707 */
708bool mei_hbm_version_is_supported(struct mei_device *dev)
709{
710 return (dev->version.major_version < HBM_MAJOR_VERSION) ||
711 (dev->version.major_version == HBM_MAJOR_VERSION &&
712 dev->version.minor_version <= HBM_MINOR_VERSION);
713}
714
bb1b0133
TW
715/**
716 * mei_hbm_dispatch - bottom half read routine after ISR to
717 * handle the read bus message cmd processing.
718 *
719 * @dev: the device structure
720 * @mei_hdr: header of bus message
544f9460
TW
721 *
722 * returns 0 on success and < 0 on failure
bb1b0133 723 */
544f9460 724int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
bb1b0133
TW
725{
726 struct mei_bus_message *mei_msg;
bb1b0133 727 struct hbm_host_version_response *version_res;
bb1b0133
TW
728 struct hbm_props_response *props_res;
729 struct hbm_host_enum_response *enum_res;
bb1b0133 730
12f45ed4
TW
731 struct mei_hbm_cl_cmd *cl_cmd;
732 struct hbm_client_connect_request *disconnect_req;
733 struct hbm_flow_control *flow_control;
734
bb1b0133
TW
735 /* read the message to our buffer */
736 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
737 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
738 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
12f45ed4 739 cl_cmd = (struct mei_hbm_cl_cmd *)mei_msg;
bb1b0133 740
66ae460b
TW
741 /* ignore spurious message and prevent reset nesting
742 * hbm is put to idle during system reset
743 */
744 if (dev->hbm_state == MEI_HBM_IDLE) {
745 dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n");
746 return 0;
747 }
748
bb1b0133
TW
749 switch (mei_msg->hbm_cmd) {
750 case HOST_START_RES_CMD:
544f9460
TW
751 dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n");
752
753 dev->init_clients_timer = 0;
754
bb1b0133 755 version_res = (struct hbm_host_version_response *)mei_msg;
2c9b48ac
TW
756
757 dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
758 HBM_MAJOR_VERSION, HBM_MINOR_VERSION,
759 version_res->me_max_version.major_version,
760 version_res->me_max_version.minor_version);
761
762 if (version_res->host_version_supported) {
763 dev->version.major_version = HBM_MAJOR_VERSION;
764 dev->version.minor_version = HBM_MINOR_VERSION;
765 } else {
766 dev->version.major_version =
767 version_res->me_max_version.major_version;
768 dev->version.minor_version =
769 version_res->me_max_version.minor_version;
770 }
771
772 if (!mei_hbm_version_is_supported(dev)) {
544f9460 773 dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
bb1b0133 774
544f9460 775 dev->hbm_state = MEI_HBM_STOPPED;
6bb948c9 776 if (mei_hbm_stop_req(dev)) {
544f9460
TW
777 dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
778 return -EIO;
779 }
780 break;
781 }
9b0d5efc 782
bae1cc7d
TW
783 mei_hbm_config_features(dev);
784
544f9460 785 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
cb02efc3 786 dev->hbm_state != MEI_HBM_STARTING) {
544f9460
TW
787 dev_err(&dev->pdev->dev, "hbm: start: state mismatch, [%d, %d]\n",
788 dev->dev_state, dev->hbm_state);
789 return -EPROTO;
e46f1874 790 }
bb1b0133 791
544f9460
TW
792 dev->hbm_state = MEI_HBM_STARTED;
793
794 if (mei_hbm_enum_clients_req(dev)) {
795 dev_err(&dev->pdev->dev, "hbm: start: failed to send enumeration request\n");
796 return -EIO;
bb1b0133
TW
797 }
798
cb02efc3 799 wake_up(&dev->wait_hbm_start);
bb1b0133
TW
800 break;
801
802 case CLIENT_CONNECT_RES_CMD:
544f9460 803 dev_dbg(&dev->pdev->dev, "hbm: client connect response: message received.\n");
12f45ed4 804 mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT);
bb1b0133
TW
805 break;
806
807 case CLIENT_DISCONNECT_RES_CMD:
544f9460 808 dev_dbg(&dev->pdev->dev, "hbm: client disconnect response: message received.\n");
12f45ed4 809 mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_DISCONNECT);
bb1b0133
TW
810 break;
811
812 case MEI_FLOW_CONTROL_CMD:
544f9460
TW
813 dev_dbg(&dev->pdev->dev, "hbm: client flow control response: message received.\n");
814
bb1b0133 815 flow_control = (struct hbm_flow_control *) mei_msg;
6bbda15f 816 mei_hbm_cl_flow_control_res(dev, flow_control);
bb1b0133
TW
817 break;
818
4fcbc99b
TW
819 case MEI_PG_ISOLATION_ENTRY_RES_CMD:
820 dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
ba9cdd0e 821 dev->pg_event = MEI_PG_EVENT_RECEIVED;
4fcbc99b
TW
822 if (waitqueue_active(&dev->wait_pg))
823 wake_up(&dev->wait_pg);
824 break;
825
826 case MEI_PG_ISOLATION_EXIT_REQ_CMD:
827 dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
ba9cdd0e 828 dev->pg_event = MEI_PG_EVENT_RECEIVED;
4fcbc99b
TW
829 if (waitqueue_active(&dev->wait_pg))
830 wake_up(&dev->wait_pg);
180ea05b
TW
831 else
832 /*
833 * If the driver is not waiting on this then
834 * this is HW initiated exit from PG.
835 * Start runtime pm resume sequence to exit from PG.
836 */
837 pm_request_resume(&dev->pdev->dev);
4fcbc99b
TW
838 break;
839
bb1b0133 840 case HOST_CLIENT_PROPERTIES_RES_CMD:
544f9460
TW
841 dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
842
843 dev->init_clients_timer = 0;
844
5ca2d388
TW
845 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
846 dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
847 dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
848 dev->dev_state, dev->hbm_state);
544f9460
TW
849 return -EPROTO;
850 }
851
bb1b0133 852 props_res = (struct hbm_props_response *)mei_msg;
bb1b0133 853
544f9460 854 if (props_res->status) {
89778d6e
TW
855 dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d %s\n",
856 props_res->status,
857 mei_hbm_status_str(props_res->status));
544f9460 858 return -EPROTO;
bb1b0133
TW
859 }
860
5ca2d388 861 mei_hbm_me_cl_add(dev, props_res);
bb1b0133 862
bb1b0133
TW
863 dev->me_client_index++;
864 dev->me_client_presentation_num++;
865
8120e720 866 /* request property for the next client */
544f9460
TW
867 if (mei_hbm_prop_req(dev))
868 return -EIO;
bb1b0133
TW
869
870 break;
871
872 case HOST_ENUM_RES_CMD:
544f9460
TW
873 dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n");
874
875 dev->init_clients_timer = 0;
876
bb1b0133 877 enum_res = (struct hbm_host_enum_response *) mei_msg;
23f5a322
TW
878 BUILD_BUG_ON(sizeof(dev->me_clients_map)
879 < sizeof(enum_res->valid_addresses));
880 memcpy(dev->me_clients_map, enum_res->valid_addresses,
5ca2d388 881 sizeof(enum_res->valid_addresses));
544f9460
TW
882
883 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
884 dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
885 dev_err(&dev->pdev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
886 dev->dev_state, dev->hbm_state);
887 return -EPROTO;
bb1b0133 888 }
544f9460 889
544f9460
TW
890 dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
891
892 /* first property request */
893 if (mei_hbm_prop_req(dev))
894 return -EIO;
895
bb1b0133
TW
896 break;
897
898 case HOST_STOP_RES_CMD:
544f9460
TW
899 dev_dbg(&dev->pdev->dev, "hbm: stop response: message received\n");
900
901 dev->init_clients_timer = 0;
902
903 if (dev->hbm_state != MEI_HBM_STOPPED) {
904 dev_err(&dev->pdev->dev, "hbm: stop response: state mismatch, [%d, %d]\n",
905 dev->dev_state, dev->hbm_state);
906 return -EPROTO;
907 }
9b0d5efc 908
33ec0826 909 dev->dev_state = MEI_DEV_POWER_DOWN;
544f9460
TW
910 dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
911 /* force the reset */
912 return -EPROTO;
bb1b0133
TW
913 break;
914
915 case CLIENT_DISCONNECT_REQ_CMD:
544f9460
TW
916 dev_dbg(&dev->pdev->dev, "hbm: disconnect request: message received\n");
917
bb1b0133 918 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
8120e720 919 mei_hbm_fw_disconnect_req(dev, disconnect_req);
bb1b0133
TW
920 break;
921
922 case ME_STOP_REQ_CMD:
544f9460 923 dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
544f9460 924 dev->hbm_state = MEI_HBM_STOPPED;
6bb948c9 925 if (mei_hbm_stop_req(dev)) {
cb02efc3 926 dev_err(&dev->pdev->dev, "hbm: stop request: failed to send stop request\n");
6bb948c9
TW
927 return -EIO;
928 }
bb1b0133 929 break;
bb1b0133
TW
930 default:
931 BUG();
932 break;
933
934 }
544f9460 935 return 0;
bb1b0133
TW
936}
937
This page took 0.172923 seconds and 5 git commands to generate.