1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2003-2022, Intel Corporation. All rights reserved. |
4 | * Intel Management Engine Interface (Intel MEI) Linux driver |
5 | */ |
6 | #include <linux/export.h> |
7 | #include <linux/sched.h> |
8 | #include <linux/wait.h> |
9 | #include <linux/pm_runtime.h> |
10 | #include <linux/slab.h> |
11 | |
12 | #include <linux/mei.h> |
13 | |
14 | #include "mei_dev.h" |
15 | #include "hbm.h" |
16 | #include "client.h" |
17 | |
18 | static const char *mei_hbm_status_str(enum mei_hbm_status status) |
19 | { |
20 | #define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status |
21 | switch (status) { |
22 | MEI_HBM_STATUS(SUCCESS); |
23 | MEI_HBM_STATUS(CLIENT_NOT_FOUND); |
24 | MEI_HBM_STATUS(ALREADY_EXISTS); |
25 | MEI_HBM_STATUS(REJECTED); |
26 | MEI_HBM_STATUS(INVALID_PARAMETER); |
27 | MEI_HBM_STATUS(NOT_ALLOWED); |
28 | MEI_HBM_STATUS(ALREADY_STARTED); |
29 | MEI_HBM_STATUS(NOT_STARTED); |
30 | default: return "unknown" ; |
31 | } |
32 | #undef MEI_HBM_STATUS |
33 | }; |
34 | |
35 | static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status) |
36 | { |
37 | #define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status |
38 | switch (status) { |
39 | MEI_CL_CS(SUCCESS); |
40 | MEI_CL_CS(NOT_FOUND); |
41 | MEI_CL_CS(ALREADY_STARTED); |
42 | MEI_CL_CS(OUT_OF_RESOURCES); |
43 | MEI_CL_CS(MESSAGE_SMALL); |
44 | MEI_CL_CS(NOT_ALLOWED); |
45 | default: return "unknown" ; |
46 | } |
47 | #undef MEI_CL_CCS |
48 | } |
49 | |
50 | const char *mei_hbm_state_str(enum mei_hbm_state state) |
51 | { |
52 | #define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state |
53 | switch (state) { |
54 | MEI_HBM_STATE(IDLE); |
55 | MEI_HBM_STATE(STARTING); |
56 | MEI_HBM_STATE(STARTED); |
57 | MEI_HBM_STATE(DR_SETUP); |
58 | MEI_HBM_STATE(ENUM_CLIENTS); |
59 | MEI_HBM_STATE(CLIENT_PROPERTIES); |
60 | MEI_HBM_STATE(STOPPED); |
61 | default: |
62 | return "unknown" ; |
63 | } |
64 | #undef MEI_HBM_STATE |
65 | } |
66 | |
67 | /** |
68 | * mei_cl_conn_status_to_errno - convert client connect response |
69 | * status to error code |
70 | * |
71 | * @status: client connect response status |
72 | * |
73 | * Return: corresponding error code |
74 | */ |
75 | static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status) |
76 | { |
77 | switch (status) { |
78 | case MEI_CL_CONN_SUCCESS: return 0; |
79 | case MEI_CL_CONN_NOT_FOUND: return -ENOTTY; |
80 | case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY; |
81 | case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY; |
82 | case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL; |
83 | case MEI_CL_CONN_NOT_ALLOWED: return -EBUSY; |
84 | default: return -EINVAL; |
85 | } |
86 | } |
87 | |
88 | /** |
89 | * mei_hbm_write_message - wrapper for sending hbm messages. |
90 | * |
91 | * @dev: mei device |
92 | * @hdr: mei header |
93 | * @data: payload |
94 | * |
95 | * Return: >=0 on success, <0 on error |
96 | */ |
97 | static inline int mei_hbm_write_message(struct mei_device *dev, |
98 | struct mei_msg_hdr *hdr, |
99 | const void *data) |
100 | { |
101 | return mei_write_message(dev, hdr, hdr_len: sizeof(*hdr), data, data_len: hdr->length); |
102 | } |
103 | |
104 | /** |
105 | * mei_hbm_idle - set hbm to idle state |
106 | * |
107 | * @dev: the device structure |
108 | */ |
109 | void mei_hbm_idle(struct mei_device *dev) |
110 | { |
111 | dev->init_clients_timer = 0; |
112 | dev->hbm_state = MEI_HBM_IDLE; |
113 | } |
114 | |
115 | /** |
116 | * mei_hbm_reset - reset hbm counters and book keeping data structures |
117 | * |
118 | * @dev: the device structure |
119 | */ |
120 | void mei_hbm_reset(struct mei_device *dev) |
121 | { |
122 | mei_me_cl_rm_all(dev); |
123 | |
124 | mei_hbm_idle(dev); |
125 | } |
126 | |
127 | /** |
128 | * mei_hbm_hdr - construct hbm header |
129 | * |
130 | * @mei_hdr: hbm header |
131 | * @length: payload length |
132 | */ |
133 | |
134 | static inline void mei_hbm_hdr(struct mei_msg_hdr *mei_hdr, size_t length) |
135 | { |
136 | memset(mei_hdr, 0, sizeof(*mei_hdr)); |
137 | mei_hdr->length = length; |
138 | mei_hdr->msg_complete = 1; |
139 | } |
140 | |
141 | /** |
142 | * mei_hbm_cl_hdr - construct client hbm header |
143 | * |
144 | * @cl: client |
145 | * @hbm_cmd: host bus message command |
146 | * @buf: buffer for cl header |
147 | * @len: buffer length |
148 | */ |
149 | static inline |
150 | void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len) |
151 | { |
152 | struct mei_hbm_cl_cmd *cmd = buf; |
153 | |
154 | memset(cmd, 0, len); |
155 | |
156 | cmd->hbm_cmd = hbm_cmd; |
157 | cmd->host_addr = mei_cl_host_addr(cl); |
158 | cmd->me_addr = mei_cl_me_id(cl); |
159 | } |
160 | |
161 | /** |
162 | * mei_hbm_cl_write - write simple hbm client message |
163 | * |
164 | * @dev: the device structure |
165 | * @cl: client |
166 | * @hbm_cmd: host bus message command |
167 | * @buf: message buffer |
168 | * @len: buffer length |
169 | * |
170 | * Return: 0 on success, <0 on failure. |
171 | */ |
172 | static inline int mei_hbm_cl_write(struct mei_device *dev, struct mei_cl *cl, |
173 | u8 hbm_cmd, void *buf, size_t len) |
174 | { |
175 | struct mei_msg_hdr mei_hdr; |
176 | |
177 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: len); |
178 | mei_hbm_cl_hdr(cl, hbm_cmd, buf, len); |
179 | |
180 | return mei_hbm_write_message(dev, hdr: &mei_hdr, data: buf); |
181 | } |
182 | |
183 | /** |
184 | * mei_hbm_cl_addr_equal - check if the client's and |
185 | * the message address match |
186 | * |
187 | * @cl: client |
188 | * @cmd: hbm client message |
189 | * |
190 | * Return: true if addresses are the same |
191 | */ |
192 | static inline |
193 | bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd) |
194 | { |
195 | return mei_cl_host_addr(cl) == cmd->host_addr && |
196 | mei_cl_me_id(cl) == cmd->me_addr; |
197 | } |
198 | |
199 | /** |
200 | * mei_hbm_cl_find_by_cmd - find recipient client |
201 | * |
202 | * @dev: the device structure |
203 | * @buf: a buffer with hbm cl command |
204 | * |
205 | * Return: the recipient client or NULL if not found |
206 | */ |
207 | static inline |
208 | struct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf) |
209 | { |
210 | struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf; |
211 | struct mei_cl *cl; |
212 | |
213 | list_for_each_entry(cl, &dev->file_list, link) |
214 | if (mei_hbm_cl_addr_equal(cl, cmd)) |
215 | return cl; |
216 | return NULL; |
217 | } |
218 | |
219 | |
220 | /** |
221 | * mei_hbm_start_wait - wait for start response message. |
222 | * |
223 | * @dev: the device structure |
224 | * |
225 | * Return: 0 on success and < 0 on failure |
226 | */ |
227 | int mei_hbm_start_wait(struct mei_device *dev) |
228 | { |
229 | int ret; |
230 | |
231 | if (dev->hbm_state > MEI_HBM_STARTING) |
232 | return 0; |
233 | |
234 | mutex_unlock(lock: &dev->device_lock); |
235 | ret = wait_event_timeout(dev->wait_hbm_start, |
236 | dev->hbm_state != MEI_HBM_STARTING, |
237 | dev->timeouts.hbm); |
238 | mutex_lock(&dev->device_lock); |
239 | |
240 | if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) { |
241 | dev->hbm_state = MEI_HBM_IDLE; |
242 | dev_err(dev->dev, "waiting for mei start failed\n" ); |
243 | return -ETIME; |
244 | } |
245 | return 0; |
246 | } |
247 | |
248 | /** |
249 | * mei_hbm_start_req - sends start request message. |
250 | * |
251 | * @dev: the device structure |
252 | * |
253 | * Return: 0 on success and < 0 on failure |
254 | */ |
255 | int mei_hbm_start_req(struct mei_device *dev) |
256 | { |
257 | struct mei_msg_hdr mei_hdr; |
258 | struct hbm_host_version_request req; |
259 | int ret; |
260 | |
261 | mei_hbm_reset(dev); |
262 | |
263 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
264 | |
265 | /* host start message */ |
266 | memset(&req, 0, sizeof(req)); |
267 | req.hbm_cmd = HOST_START_REQ_CMD; |
268 | req.host_version.major_version = HBM_MAJOR_VERSION; |
269 | req.host_version.minor_version = HBM_MINOR_VERSION; |
270 | |
271 | dev->hbm_state = MEI_HBM_IDLE; |
272 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
273 | if (ret) { |
274 | dev_err(dev->dev, "version message write failed: ret = %d\n" , |
275 | ret); |
276 | return ret; |
277 | } |
278 | |
279 | dev->hbm_state = MEI_HBM_STARTING; |
280 | dev->init_clients_timer = dev->timeouts.client_init; |
281 | mei_schedule_stall_timer(dev); |
282 | return 0; |
283 | } |
284 | |
285 | /** |
286 | * mei_hbm_dma_setup_req() - setup DMA request |
287 | * @dev: the device structure |
288 | * |
289 | * Return: 0 on success and < 0 on failure |
290 | */ |
291 | static int mei_hbm_dma_setup_req(struct mei_device *dev) |
292 | { |
293 | struct mei_msg_hdr mei_hdr; |
294 | struct hbm_dma_setup_request req; |
295 | unsigned int i; |
296 | int ret; |
297 | |
298 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
299 | |
300 | memset(&req, 0, sizeof(req)); |
301 | req.hbm_cmd = MEI_HBM_DMA_SETUP_REQ_CMD; |
302 | for (i = 0; i < DMA_DSCR_NUM; i++) { |
303 | phys_addr_t paddr; |
304 | |
305 | paddr = dev->dr_dscr[i].daddr; |
306 | req.dma_dscr[i].addr_hi = upper_32_bits(paddr); |
307 | req.dma_dscr[i].addr_lo = lower_32_bits(paddr); |
308 | req.dma_dscr[i].size = dev->dr_dscr[i].size; |
309 | } |
310 | |
311 | mei_dma_ring_reset(dev); |
312 | |
313 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
314 | if (ret) { |
315 | dev_err(dev->dev, "dma setup request write failed: ret = %d.\n" , |
316 | ret); |
317 | return ret; |
318 | } |
319 | |
320 | dev->hbm_state = MEI_HBM_DR_SETUP; |
321 | dev->init_clients_timer = dev->timeouts.client_init; |
322 | mei_schedule_stall_timer(dev); |
323 | return 0; |
324 | } |
325 | |
326 | /** |
327 | * mei_hbm_capabilities_req - request capabilities |
328 | * |
329 | * @dev: the device structure |
330 | * |
331 | * Return: 0 on success and < 0 on failure |
332 | */ |
333 | static int mei_hbm_capabilities_req(struct mei_device *dev) |
334 | { |
335 | struct mei_msg_hdr mei_hdr; |
336 | struct hbm_capability_request req; |
337 | int ret; |
338 | |
339 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
340 | |
341 | memset(&req, 0, sizeof(req)); |
342 | req.hbm_cmd = MEI_HBM_CAPABILITIES_REQ_CMD; |
343 | if (dev->hbm_f_vt_supported) |
344 | req.capability_requested[0] |= HBM_CAP_VT; |
345 | |
346 | if (dev->hbm_f_cd_supported) |
347 | req.capability_requested[0] |= HBM_CAP_CD; |
348 | |
349 | if (dev->hbm_f_gsc_supported) |
350 | req.capability_requested[0] |= HBM_CAP_GSC; |
351 | |
352 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
353 | if (ret) { |
354 | dev_err(dev->dev, |
355 | "capabilities request write failed: ret = %d.\n" , ret); |
356 | return ret; |
357 | } |
358 | |
359 | dev->hbm_state = MEI_HBM_CAP_SETUP; |
360 | dev->init_clients_timer = dev->timeouts.client_init; |
361 | mei_schedule_stall_timer(dev); |
362 | return 0; |
363 | } |
364 | |
365 | /** |
366 | * mei_hbm_enum_clients_req - sends enumeration client request message. |
367 | * |
368 | * @dev: the device structure |
369 | * |
370 | * Return: 0 on success and < 0 on failure |
371 | */ |
372 | static int mei_hbm_enum_clients_req(struct mei_device *dev) |
373 | { |
374 | struct mei_msg_hdr mei_hdr; |
375 | struct hbm_host_enum_request req; |
376 | int ret; |
377 | |
378 | /* enumerate clients */ |
379 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
380 | |
381 | memset(&req, 0, sizeof(req)); |
382 | req.hbm_cmd = HOST_ENUM_REQ_CMD; |
383 | req.flags |= dev->hbm_f_dc_supported ? MEI_HBM_ENUM_F_ALLOW_ADD : 0; |
384 | req.flags |= dev->hbm_f_ie_supported ? |
385 | MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0; |
386 | |
387 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
388 | if (ret) { |
389 | dev_err(dev->dev, "enumeration request write failed: ret = %d.\n" , |
390 | ret); |
391 | return ret; |
392 | } |
393 | dev->hbm_state = MEI_HBM_ENUM_CLIENTS; |
394 | dev->init_clients_timer = dev->timeouts.client_init; |
395 | mei_schedule_stall_timer(dev); |
396 | return 0; |
397 | } |
398 | |
399 | /** |
400 | * mei_hbm_me_cl_add - add new me client to the list |
401 | * |
402 | * @dev: the device structure |
403 | * @res: hbm property response |
404 | * |
405 | * Return: 0 on success and -ENOMEM on allocation failure |
406 | */ |
407 | |
408 | static int mei_hbm_me_cl_add(struct mei_device *dev, |
409 | struct hbm_props_response *res) |
410 | { |
411 | struct mei_me_client *me_cl; |
412 | const uuid_le *uuid = &res->client_properties.protocol_name; |
413 | |
414 | mei_me_cl_rm_by_uuid(dev, uuid); |
415 | |
416 | me_cl = kzalloc(size: sizeof(*me_cl), GFP_KERNEL); |
417 | if (!me_cl) |
418 | return -ENOMEM; |
419 | |
420 | mei_me_cl_init(me_cl); |
421 | |
422 | me_cl->props = res->client_properties; |
423 | me_cl->client_id = res->me_addr; |
424 | me_cl->tx_flow_ctrl_creds = 0; |
425 | |
426 | mei_me_cl_add(dev, me_cl); |
427 | |
428 | return 0; |
429 | } |
430 | |
431 | /** |
432 | * mei_hbm_add_cl_resp - send response to fw on client add request |
433 | * |
434 | * @dev: the device structure |
435 | * @addr: me address |
436 | * @status: response status |
437 | * |
438 | * Return: 0 on success and < 0 on failure |
439 | */ |
440 | static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status) |
441 | { |
442 | struct mei_msg_hdr mei_hdr; |
443 | struct hbm_add_client_response resp; |
444 | int ret; |
445 | |
446 | dev_dbg(dev->dev, "adding client response\n" ); |
447 | |
448 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(resp)); |
449 | |
450 | memset(&resp, 0, sizeof(resp)); |
451 | resp.hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD; |
452 | resp.me_addr = addr; |
453 | resp.status = status; |
454 | |
455 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &resp); |
456 | if (ret) |
457 | dev_err(dev->dev, "add client response write failed: ret = %d\n" , |
458 | ret); |
459 | return ret; |
460 | } |
461 | |
462 | /** |
463 | * mei_hbm_fw_add_cl_req - request from the fw to add a client |
464 | * |
465 | * @dev: the device structure |
466 | * @req: add client request |
467 | * |
468 | * Return: 0 on success and < 0 on failure |
469 | */ |
470 | static int mei_hbm_fw_add_cl_req(struct mei_device *dev, |
471 | struct hbm_add_client_request *req) |
472 | { |
473 | int ret; |
474 | u8 status = MEI_HBMS_SUCCESS; |
475 | |
476 | BUILD_BUG_ON(sizeof(struct hbm_add_client_request) != |
477 | sizeof(struct hbm_props_response)); |
478 | |
479 | ret = mei_hbm_me_cl_add(dev, res: (struct hbm_props_response *)req); |
480 | if (ret) |
481 | status = !MEI_HBMS_SUCCESS; |
482 | |
483 | if (dev->dev_state == MEI_DEV_ENABLED) |
484 | schedule_work(work: &dev->bus_rescan_work); |
485 | |
486 | return mei_hbm_add_cl_resp(dev, addr: req->me_addr, status); |
487 | } |
488 | |
489 | /** |
490 | * mei_hbm_cl_notify_req - send notification request |
491 | * |
492 | * @dev: the device structure |
493 | * @cl: a client to disconnect from |
494 | * @start: true for start false for stop |
495 | * |
496 | * Return: 0 on success and -EIO on write failure |
497 | */ |
498 | int mei_hbm_cl_notify_req(struct mei_device *dev, |
499 | struct mei_cl *cl, u8 start) |
500 | { |
501 | |
502 | struct mei_msg_hdr mei_hdr; |
503 | struct hbm_notification_request req; |
504 | int ret; |
505 | |
506 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
507 | mei_hbm_cl_hdr(cl, MEI_HBM_NOTIFY_REQ_CMD, buf: &req, len: sizeof(req)); |
508 | |
509 | req.start = start; |
510 | |
511 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
512 | if (ret) |
513 | dev_err(dev->dev, "notify request failed: ret = %d\n" , ret); |
514 | |
515 | return ret; |
516 | } |
517 | |
518 | /** |
519 | * notify_res_to_fop - convert notification response to the proper |
520 | * notification FOP |
521 | * |
522 | * @cmd: client notification start response command |
523 | * |
524 | * Return: MEI_FOP_NOTIFY_START or MEI_FOP_NOTIFY_STOP; |
525 | */ |
526 | static inline enum mei_cb_file_ops notify_res_to_fop(struct mei_hbm_cl_cmd *cmd) |
527 | { |
528 | struct hbm_notification_response *rs = |
529 | (struct hbm_notification_response *)cmd; |
530 | |
531 | return mei_cl_notify_req2fop(request: rs->start); |
532 | } |
533 | |
534 | /** |
535 | * mei_hbm_cl_notify_start_res - update the client state according |
536 | * notify start response |
537 | * |
538 | * @dev: the device structure |
539 | * @cl: mei host client |
540 | * @cmd: client notification start response command |
541 | */ |
542 | static void mei_hbm_cl_notify_start_res(struct mei_device *dev, |
543 | struct mei_cl *cl, |
544 | struct mei_hbm_cl_cmd *cmd) |
545 | { |
546 | struct hbm_notification_response *rs = |
547 | (struct hbm_notification_response *)cmd; |
548 | |
549 | cl_dbg(dev, cl, "hbm: notify start response status=%d\n" , rs->status); |
550 | |
551 | if (rs->status == MEI_HBMS_SUCCESS || |
552 | rs->status == MEI_HBMS_ALREADY_STARTED) { |
553 | cl->notify_en = true; |
554 | cl->status = 0; |
555 | } else { |
556 | cl->status = -EINVAL; |
557 | } |
558 | } |
559 | |
560 | /** |
561 | * mei_hbm_cl_notify_stop_res - update the client state according |
562 | * notify stop response |
563 | * |
564 | * @dev: the device structure |
565 | * @cl: mei host client |
566 | * @cmd: client notification stop response command |
567 | */ |
568 | static void mei_hbm_cl_notify_stop_res(struct mei_device *dev, |
569 | struct mei_cl *cl, |
570 | struct mei_hbm_cl_cmd *cmd) |
571 | { |
572 | struct hbm_notification_response *rs = |
573 | (struct hbm_notification_response *)cmd; |
574 | |
575 | cl_dbg(dev, cl, "hbm: notify stop response status=%d\n" , rs->status); |
576 | |
577 | if (rs->status == MEI_HBMS_SUCCESS || |
578 | rs->status == MEI_HBMS_NOT_STARTED) { |
579 | cl->notify_en = false; |
580 | cl->status = 0; |
581 | } else { |
582 | /* TODO: spec is not clear yet about other possible issues */ |
583 | cl->status = -EINVAL; |
584 | } |
585 | } |
586 | |
587 | /** |
588 | * mei_hbm_cl_notify - signal notification event |
589 | * |
590 | * @dev: the device structure |
591 | * @cmd: notification client message |
592 | */ |
593 | static void mei_hbm_cl_notify(struct mei_device *dev, |
594 | struct mei_hbm_cl_cmd *cmd) |
595 | { |
596 | struct mei_cl *cl; |
597 | |
598 | cl = mei_hbm_cl_find_by_cmd(dev, buf: cmd); |
599 | if (cl) |
600 | mei_cl_notify(cl); |
601 | } |
602 | |
603 | /** |
604 | * mei_hbm_cl_dma_map_req - send client dma map request |
605 | * |
606 | * @dev: the device structure |
607 | * @cl: mei host client |
608 | * |
609 | * Return: 0 on success and -EIO on write failure |
610 | */ |
611 | int mei_hbm_cl_dma_map_req(struct mei_device *dev, struct mei_cl *cl) |
612 | { |
613 | struct mei_msg_hdr mei_hdr; |
614 | struct hbm_client_dma_map_request req; |
615 | int ret; |
616 | |
617 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
618 | |
619 | memset(&req, 0, sizeof(req)); |
620 | |
621 | req.hbm_cmd = MEI_HBM_CLIENT_DMA_MAP_REQ_CMD; |
622 | req.client_buffer_id = cl->dma.buffer_id; |
623 | req.address_lsb = lower_32_bits(cl->dma.daddr); |
624 | req.address_msb = upper_32_bits(cl->dma.daddr); |
625 | req.size = cl->dma.size; |
626 | |
627 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
628 | if (ret) |
629 | dev_err(dev->dev, "dma map request failed: ret = %d\n" , ret); |
630 | |
631 | return ret; |
632 | } |
633 | |
634 | /** |
635 | * mei_hbm_cl_dma_unmap_req - send client dma unmap request |
636 | * |
637 | * @dev: the device structure |
638 | * @cl: mei host client |
639 | * |
640 | * Return: 0 on success and -EIO on write failure |
641 | */ |
642 | int mei_hbm_cl_dma_unmap_req(struct mei_device *dev, struct mei_cl *cl) |
643 | { |
644 | struct mei_msg_hdr mei_hdr; |
645 | struct hbm_client_dma_unmap_request req; |
646 | int ret; |
647 | |
648 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
649 | |
650 | memset(&req, 0, sizeof(req)); |
651 | |
652 | req.hbm_cmd = MEI_HBM_CLIENT_DMA_UNMAP_REQ_CMD; |
653 | req.client_buffer_id = cl->dma.buffer_id; |
654 | |
655 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
656 | if (ret) |
657 | dev_err(dev->dev, "dma unmap request failed: ret = %d\n" , ret); |
658 | |
659 | return ret; |
660 | } |
661 | |
662 | static void mei_hbm_cl_dma_map_res(struct mei_device *dev, |
663 | struct hbm_client_dma_response *res) |
664 | { |
665 | struct mei_cl *cl; |
666 | struct mei_cl_cb *cb, *next; |
667 | |
668 | cl = NULL; |
669 | list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) { |
670 | if (cb->fop_type != MEI_FOP_DMA_MAP) |
671 | continue; |
672 | if (!cb->cl->dma.buffer_id || cb->cl->dma_mapped) |
673 | continue; |
674 | |
675 | cl = cb->cl; |
676 | break; |
677 | } |
678 | if (!cl) |
679 | return; |
680 | |
681 | if (res->status) { |
682 | dev_err(dev->dev, "cl dma map failed %d\n" , res->status); |
683 | cl->status = -EFAULT; |
684 | } else { |
685 | dev_dbg(dev->dev, "cl dma map succeeded\n" ); |
686 | cl->dma_mapped = 1; |
687 | cl->status = 0; |
688 | } |
689 | wake_up(&cl->wait); |
690 | } |
691 | |
692 | static void mei_hbm_cl_dma_unmap_res(struct mei_device *dev, |
693 | struct hbm_client_dma_response *res) |
694 | { |
695 | struct mei_cl *cl; |
696 | struct mei_cl_cb *cb, *next; |
697 | |
698 | cl = NULL; |
699 | list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) { |
700 | if (cb->fop_type != MEI_FOP_DMA_UNMAP) |
701 | continue; |
702 | if (!cb->cl->dma.buffer_id || !cb->cl->dma_mapped) |
703 | continue; |
704 | |
705 | cl = cb->cl; |
706 | break; |
707 | } |
708 | if (!cl) |
709 | return; |
710 | |
711 | if (res->status) { |
712 | dev_err(dev->dev, "cl dma unmap failed %d\n" , res->status); |
713 | cl->status = -EFAULT; |
714 | } else { |
715 | dev_dbg(dev->dev, "cl dma unmap succeeded\n" ); |
716 | cl->dma_mapped = 0; |
717 | cl->status = 0; |
718 | } |
719 | wake_up(&cl->wait); |
720 | } |
721 | |
722 | /** |
723 | * mei_hbm_prop_req - request property for a single client |
724 | * |
725 | * @dev: the device structure |
726 | * @start_idx: client index to start search |
727 | * |
728 | * Return: 0 on success and < 0 on failure |
729 | */ |
730 | static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx) |
731 | { |
732 | struct mei_msg_hdr mei_hdr; |
733 | struct hbm_props_request req; |
734 | unsigned long addr; |
735 | int ret; |
736 | |
737 | addr = find_next_bit(addr: dev->me_clients_map, MEI_CLIENTS_MAX, offset: start_idx); |
738 | |
739 | /* We got all client properties */ |
740 | if (addr == MEI_CLIENTS_MAX) { |
741 | dev->hbm_state = MEI_HBM_STARTED; |
742 | mei_host_client_init(dev); |
743 | return 0; |
744 | } |
745 | |
746 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
747 | |
748 | memset(&req, 0, sizeof(req)); |
749 | |
750 | req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; |
751 | req.me_addr = addr; |
752 | |
753 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
754 | if (ret) { |
755 | dev_err(dev->dev, "properties request write failed: ret = %d\n" , |
756 | ret); |
757 | return ret; |
758 | } |
759 | |
760 | dev->init_clients_timer = dev->timeouts.client_init; |
761 | mei_schedule_stall_timer(dev); |
762 | |
763 | return 0; |
764 | } |
765 | |
766 | /** |
767 | * mei_hbm_pg - sends pg command |
768 | * |
769 | * @dev: the device structure |
770 | * @pg_cmd: the pg command code |
771 | * |
772 | * Return: -EIO on write failure |
773 | * -EOPNOTSUPP if the operation is not supported by the protocol |
774 | */ |
775 | int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) |
776 | { |
777 | struct mei_msg_hdr mei_hdr; |
778 | struct hbm_power_gate req; |
779 | int ret; |
780 | |
781 | if (!dev->hbm_f_pg_supported) |
782 | return -EOPNOTSUPP; |
783 | |
784 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
785 | |
786 | memset(&req, 0, sizeof(req)); |
787 | req.hbm_cmd = pg_cmd; |
788 | |
789 | ret = mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
790 | if (ret) |
791 | dev_err(dev->dev, "power gate command write failed.\n" ); |
792 | return ret; |
793 | } |
794 | EXPORT_SYMBOL_GPL(mei_hbm_pg); |
795 | |
796 | /** |
797 | * mei_hbm_stop_req - send stop request message |
798 | * |
799 | * @dev: mei device |
800 | * |
801 | * Return: -EIO on write failure |
802 | */ |
803 | static int mei_hbm_stop_req(struct mei_device *dev) |
804 | { |
805 | struct mei_msg_hdr mei_hdr; |
806 | struct hbm_host_stop_request req; |
807 | |
808 | mei_hbm_hdr(mei_hdr: &mei_hdr, length: sizeof(req)); |
809 | |
810 | memset(&req, 0, sizeof(req)); |
811 | req.hbm_cmd = HOST_STOP_REQ_CMD; |
812 | req.reason = DRIVER_STOP_REQUEST; |
813 | |
814 | return mei_hbm_write_message(dev, hdr: &mei_hdr, data: &req); |
815 | } |
816 | |
817 | /** |
818 | * mei_hbm_cl_flow_control_req - sends flow control request. |
819 | * |
820 | * @dev: the device structure |
821 | * @cl: client info |
822 | * |
823 | * Return: -EIO on write failure |
824 | */ |
825 | int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) |
826 | { |
827 | struct hbm_flow_control req; |
828 | |
829 | cl_dbg(dev, cl, "sending flow control\n" ); |
830 | return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, |
831 | buf: &req, len: sizeof(req)); |
832 | } |
833 | |
834 | /** |
835 | * mei_hbm_add_single_tx_flow_ctrl_creds - adds single buffer credentials. |
836 | * |
837 | * @dev: the device structure |
838 | * @fctrl: flow control response bus message |
839 | * |
840 | * Return: 0 on success, < 0 otherwise |
841 | */ |
842 | static int mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device *dev, |
843 | struct hbm_flow_control *fctrl) |
844 | { |
845 | struct mei_me_client *me_cl; |
846 | int rets; |
847 | |
848 | me_cl = mei_me_cl_by_id(dev, client_id: fctrl->me_addr); |
849 | if (!me_cl) { |
850 | dev_err(dev->dev, "no such me client %d\n" , fctrl->me_addr); |
851 | return -ENOENT; |
852 | } |
853 | |
854 | if (WARN_ON(me_cl->props.single_recv_buf == 0)) { |
855 | rets = -EINVAL; |
856 | goto out; |
857 | } |
858 | |
859 | me_cl->tx_flow_ctrl_creds++; |
860 | dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n" , |
861 | fctrl->me_addr, me_cl->tx_flow_ctrl_creds); |
862 | |
863 | rets = 0; |
864 | out: |
865 | mei_me_cl_put(me_cl); |
866 | return rets; |
867 | } |
868 | |
869 | /** |
870 | * mei_hbm_cl_tx_flow_ctrl_creds_res - flow control response from me |
871 | * |
872 | * @dev: the device structure |
873 | * @fctrl: flow control response bus message |
874 | */ |
875 | static void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev, |
876 | struct hbm_flow_control *fctrl) |
877 | { |
878 | struct mei_cl *cl; |
879 | |
880 | if (!fctrl->host_addr) { |
881 | /* single receive buffer */ |
882 | mei_hbm_add_single_tx_flow_ctrl_creds(dev, fctrl); |
883 | return; |
884 | } |
885 | |
886 | cl = mei_hbm_cl_find_by_cmd(dev, buf: fctrl); |
887 | if (cl) { |
888 | cl->tx_flow_ctrl_creds++; |
889 | cl_dbg(dev, cl, "flow control creds = %d.\n" , |
890 | cl->tx_flow_ctrl_creds); |
891 | } |
892 | } |
893 | |
894 | |
895 | /** |
896 | * mei_hbm_cl_disconnect_req - sends disconnect message to fw. |
897 | * |
898 | * @dev: the device structure |
899 | * @cl: a client to disconnect from |
900 | * |
901 | * Return: -EIO on write failure |
902 | */ |
903 | int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) |
904 | { |
905 | struct hbm_client_connect_request req; |
906 | |
907 | return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, |
908 | buf: &req, len: sizeof(req)); |
909 | } |
910 | |
911 | /** |
912 | * mei_hbm_cl_disconnect_rsp - sends disconnect response to the FW |
913 | * |
914 | * @dev: the device structure |
915 | * @cl: a client to disconnect from |
916 | * |
917 | * Return: -EIO on write failure |
918 | */ |
919 | int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl) |
920 | { |
921 | struct hbm_client_connect_response resp; |
922 | |
923 | return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, |
924 | buf: &resp, len: sizeof(resp)); |
925 | } |
926 | |
927 | /** |
928 | * mei_hbm_cl_disconnect_res - update the client state according |
929 | * disconnect response |
930 | * |
931 | * @dev: the device structure |
932 | * @cl: mei host client |
933 | * @cmd: disconnect client response host bus message |
934 | */ |
935 | static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl, |
936 | struct mei_hbm_cl_cmd *cmd) |
937 | { |
938 | struct hbm_client_connect_response *rs = |
939 | (struct hbm_client_connect_response *)cmd; |
940 | |
941 | cl_dbg(dev, cl, "hbm: disconnect response status=%d\n" , rs->status); |
942 | |
943 | if (rs->status == MEI_CL_DISCONN_SUCCESS) |
944 | cl->state = MEI_FILE_DISCONNECT_REPLY; |
945 | cl->status = 0; |
946 | } |
947 | |
948 | /** |
949 | * mei_hbm_cl_connect_req - send connection request to specific me client |
950 | * |
951 | * @dev: the device structure |
952 | * @cl: a client to connect to |
953 | * |
954 | * Return: -EIO on write failure |
955 | */ |
956 | int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl) |
957 | { |
958 | struct hbm_client_connect_request req; |
959 | |
960 | return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, |
961 | buf: &req, len: sizeof(req)); |
962 | } |
963 | |
964 | /** |
965 | * mei_hbm_cl_connect_res - update the client state according |
966 | * connection response |
967 | * |
968 | * @dev: the device structure |
969 | * @cl: mei host client |
970 | * @cmd: connect client response host bus message |
971 | */ |
972 | static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl, |
973 | struct mei_hbm_cl_cmd *cmd) |
974 | { |
975 | struct hbm_client_connect_response *rs = |
976 | (struct hbm_client_connect_response *)cmd; |
977 | |
978 | cl_dbg(dev, cl, "hbm: connect response status=%s\n" , |
979 | mei_cl_conn_status_str(rs->status)); |
980 | |
981 | if (rs->status == MEI_CL_CONN_SUCCESS) |
982 | cl->state = MEI_FILE_CONNECTED; |
983 | else { |
984 | cl->state = MEI_FILE_DISCONNECT_REPLY; |
985 | if (rs->status == MEI_CL_CONN_NOT_FOUND) { |
986 | mei_me_cl_del(dev, me_cl: cl->me_cl); |
987 | if (dev->dev_state == MEI_DEV_ENABLED) |
988 | schedule_work(work: &dev->bus_rescan_work); |
989 | } |
990 | } |
991 | cl->status = mei_cl_conn_status_to_errno(status: rs->status); |
992 | } |
993 | |
994 | /** |
995 | * mei_hbm_cl_res - process hbm response received on behalf |
996 | * an client |
997 | * |
998 | * @dev: the device structure |
999 | * @rs: hbm client message |
1000 | * @fop_type: file operation type |
1001 | */ |
1002 | static void mei_hbm_cl_res(struct mei_device *dev, |
1003 | struct mei_hbm_cl_cmd *rs, |
1004 | enum mei_cb_file_ops fop_type) |
1005 | { |
1006 | struct mei_cl *cl; |
1007 | struct mei_cl_cb *cb, *next; |
1008 | |
1009 | cl = NULL; |
1010 | list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) { |
1011 | |
1012 | cl = cb->cl; |
1013 | |
1014 | if (cb->fop_type != fop_type) |
1015 | continue; |
1016 | |
1017 | if (mei_hbm_cl_addr_equal(cl, cmd: rs)) { |
1018 | list_del_init(entry: &cb->list); |
1019 | break; |
1020 | } |
1021 | } |
1022 | |
1023 | if (!cl) |
1024 | return; |
1025 | |
1026 | switch (fop_type) { |
1027 | case MEI_FOP_CONNECT: |
1028 | mei_hbm_cl_connect_res(dev, cl, cmd: rs); |
1029 | break; |
1030 | case MEI_FOP_DISCONNECT: |
1031 | mei_hbm_cl_disconnect_res(dev, cl, cmd: rs); |
1032 | break; |
1033 | case MEI_FOP_NOTIFY_START: |
1034 | mei_hbm_cl_notify_start_res(dev, cl, cmd: rs); |
1035 | break; |
1036 | case MEI_FOP_NOTIFY_STOP: |
1037 | mei_hbm_cl_notify_stop_res(dev, cl, cmd: rs); |
1038 | break; |
1039 | default: |
1040 | return; |
1041 | } |
1042 | |
1043 | cl->timer_count = 0; |
1044 | wake_up(&cl->wait); |
1045 | } |
1046 | |
1047 | |
1048 | /** |
1049 | * mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware |
1050 | * host sends disconnect response |
1051 | * |
1052 | * @dev: the device structure. |
1053 | * @disconnect_req: disconnect request bus message from the me |
1054 | * |
1055 | * Return: -ENOMEM on allocation failure |
1056 | */ |
1057 | static int mei_hbm_fw_disconnect_req(struct mei_device *dev, |
1058 | struct hbm_client_connect_request *disconnect_req) |
1059 | { |
1060 | struct mei_cl *cl; |
1061 | struct mei_cl_cb *cb; |
1062 | |
1063 | cl = mei_hbm_cl_find_by_cmd(dev, buf: disconnect_req); |
1064 | if (cl) { |
1065 | cl_warn(dev, cl, "fw disconnect request received\n" ); |
1066 | cl->state = MEI_FILE_DISCONNECTING; |
1067 | cl->timer_count = 0; |
1068 | |
1069 | cb = mei_cl_enqueue_ctrl_wr_cb(cl, length: 0, type: MEI_FOP_DISCONNECT_RSP, |
1070 | NULL); |
1071 | if (!cb) |
1072 | return -ENOMEM; |
1073 | } |
1074 | return 0; |
1075 | } |
1076 | |
1077 | /** |
1078 | * mei_hbm_pg_enter_res - PG enter response received |
1079 | * |
1080 | * @dev: the device structure. |
1081 | * |
1082 | * Return: 0 on success, -EPROTO on state mismatch |
1083 | */ |
1084 | static int mei_hbm_pg_enter_res(struct mei_device *dev) |
1085 | { |
1086 | if (mei_pg_state(dev) != MEI_PG_OFF || |
1087 | dev->pg_event != MEI_PG_EVENT_WAIT) { |
1088 | dev_err(dev->dev, "hbm: pg entry response: state mismatch [%s, %d]\n" , |
1089 | mei_pg_state_str(mei_pg_state(dev)), dev->pg_event); |
1090 | return -EPROTO; |
1091 | } |
1092 | |
1093 | dev->pg_event = MEI_PG_EVENT_RECEIVED; |
1094 | wake_up(&dev->wait_pg); |
1095 | |
1096 | return 0; |
1097 | } |
1098 | |
1099 | /** |
1100 | * mei_hbm_pg_resume - process with PG resume |
1101 | * |
1102 | * @dev: the device structure. |
1103 | */ |
1104 | void mei_hbm_pg_resume(struct mei_device *dev) |
1105 | { |
1106 | pm_request_resume(dev: dev->dev); |
1107 | } |
1108 | EXPORT_SYMBOL_GPL(mei_hbm_pg_resume); |
1109 | |
1110 | /** |
1111 | * mei_hbm_pg_exit_res - PG exit response received |
1112 | * |
1113 | * @dev: the device structure. |
1114 | * |
1115 | * Return: 0 on success, -EPROTO on state mismatch |
1116 | */ |
1117 | static int mei_hbm_pg_exit_res(struct mei_device *dev) |
1118 | { |
1119 | if (mei_pg_state(dev) != MEI_PG_ON || |
1120 | (dev->pg_event != MEI_PG_EVENT_WAIT && |
1121 | dev->pg_event != MEI_PG_EVENT_IDLE)) { |
1122 | dev_err(dev->dev, "hbm: pg exit response: state mismatch [%s, %d]\n" , |
1123 | mei_pg_state_str(mei_pg_state(dev)), dev->pg_event); |
1124 | return -EPROTO; |
1125 | } |
1126 | |
1127 | switch (dev->pg_event) { |
1128 | case MEI_PG_EVENT_WAIT: |
1129 | dev->pg_event = MEI_PG_EVENT_RECEIVED; |
1130 | wake_up(&dev->wait_pg); |
1131 | break; |
1132 | case MEI_PG_EVENT_IDLE: |
1133 | /* |
1134 | * If the driver is not waiting on this then |
1135 | * this is HW initiated exit from PG. |
1136 | * Start runtime pm resume sequence to exit from PG. |
1137 | */ |
1138 | dev->pg_event = MEI_PG_EVENT_RECEIVED; |
1139 | mei_hbm_pg_resume(dev); |
1140 | break; |
1141 | default: |
1142 | WARN(1, "hbm: pg exit response: unexpected pg event = %d\n" , |
1143 | dev->pg_event); |
1144 | return -EPROTO; |
1145 | } |
1146 | |
1147 | return 0; |
1148 | } |
1149 | |
1150 | /** |
1151 | * mei_hbm_config_features - check what hbm features and commands |
1152 | * are supported by the fw |
1153 | * |
1154 | * @dev: the device structure |
1155 | */ |
1156 | static void mei_hbm_config_features(struct mei_device *dev) |
1157 | { |
1158 | /* Power Gating Isolation Support */ |
1159 | dev->hbm_f_pg_supported = 0; |
1160 | if (dev->version.major_version > HBM_MAJOR_VERSION_PGI) |
1161 | dev->hbm_f_pg_supported = 1; |
1162 | |
1163 | if (dev->version.major_version == HBM_MAJOR_VERSION_PGI && |
1164 | dev->version.minor_version >= HBM_MINOR_VERSION_PGI) |
1165 | dev->hbm_f_pg_supported = 1; |
1166 | |
1167 | dev->hbm_f_dc_supported = 0; |
1168 | if (dev->version.major_version >= HBM_MAJOR_VERSION_DC) |
1169 | dev->hbm_f_dc_supported = 1; |
1170 | |
1171 | dev->hbm_f_ie_supported = 0; |
1172 | if (dev->version.major_version >= HBM_MAJOR_VERSION_IE) |
1173 | dev->hbm_f_ie_supported = 1; |
1174 | |
1175 | /* disconnect on connect timeout instead of link reset */ |
1176 | dev->hbm_f_dot_supported = 0; |
1177 | if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT) |
1178 | dev->hbm_f_dot_supported = 1; |
1179 | |
1180 | /* Notification Event Support */ |
1181 | dev->hbm_f_ev_supported = 0; |
1182 | if (dev->version.major_version >= HBM_MAJOR_VERSION_EV) |
1183 | dev->hbm_f_ev_supported = 1; |
1184 | |
1185 | /* Fixed Address Client Support */ |
1186 | dev->hbm_f_fa_supported = 0; |
1187 | if (dev->version.major_version >= HBM_MAJOR_VERSION_FA) |
1188 | dev->hbm_f_fa_supported = 1; |
1189 | |
1190 | /* OS ver message Support */ |
1191 | dev->hbm_f_os_supported = 0; |
1192 | if (dev->version.major_version >= HBM_MAJOR_VERSION_OS) |
1193 | dev->hbm_f_os_supported = 1; |
1194 | |
1195 | /* DMA Ring Support */ |
1196 | dev->hbm_f_dr_supported = 0; |
1197 | if (dev->version.major_version > HBM_MAJOR_VERSION_DR || |
1198 | (dev->version.major_version == HBM_MAJOR_VERSION_DR && |
1199 | dev->version.minor_version >= HBM_MINOR_VERSION_DR)) |
1200 | dev->hbm_f_dr_supported = 1; |
1201 | |
1202 | /* VTag Support */ |
1203 | dev->hbm_f_vt_supported = 0; |
1204 | if (dev->version.major_version > HBM_MAJOR_VERSION_VT || |
1205 | (dev->version.major_version == HBM_MAJOR_VERSION_VT && |
1206 | dev->version.minor_version >= HBM_MINOR_VERSION_VT)) |
1207 | dev->hbm_f_vt_supported = 1; |
1208 | |
1209 | /* GSC support */ |
1210 | if (dev->version.major_version > HBM_MAJOR_VERSION_GSC || |
1211 | (dev->version.major_version == HBM_MAJOR_VERSION_GSC && |
1212 | dev->version.minor_version >= HBM_MINOR_VERSION_GSC)) |
1213 | dev->hbm_f_gsc_supported = 1; |
1214 | |
1215 | /* Capability message Support */ |
1216 | dev->hbm_f_cap_supported = 0; |
1217 | if (dev->version.major_version > HBM_MAJOR_VERSION_CAP || |
1218 | (dev->version.major_version == HBM_MAJOR_VERSION_CAP && |
1219 | dev->version.minor_version >= HBM_MINOR_VERSION_CAP)) |
1220 | dev->hbm_f_cap_supported = 1; |
1221 | |
1222 | /* Client DMA Support */ |
1223 | dev->hbm_f_cd_supported = 0; |
1224 | if (dev->version.major_version > HBM_MAJOR_VERSION_CD || |
1225 | (dev->version.major_version == HBM_MAJOR_VERSION_CD && |
1226 | dev->version.minor_version >= HBM_MINOR_VERSION_CD)) |
1227 | dev->hbm_f_cd_supported = 1; |
1228 | } |
1229 | |
1230 | /** |
1231 | * mei_hbm_version_is_supported - checks whether the driver can |
1232 | * support the hbm version of the device |
1233 | * |
1234 | * @dev: the device structure |
1235 | * Return: true if driver can support hbm version of the device |
1236 | */ |
1237 | bool mei_hbm_version_is_supported(struct mei_device *dev) |
1238 | { |
1239 | return (dev->version.major_version < HBM_MAJOR_VERSION) || |
1240 | (dev->version.major_version == HBM_MAJOR_VERSION && |
1241 | dev->version.minor_version <= HBM_MINOR_VERSION); |
1242 | } |
1243 | |
1244 | /** |
1245 | * mei_hbm_dispatch - bottom half read routine after ISR to |
1246 | * handle the read bus message cmd processing. |
1247 | * |
1248 | * @dev: the device structure |
1249 | * @hdr: header of bus message |
1250 | * |
1251 | * Return: 0 on success and < 0 on failure |
1252 | */ |
1253 | int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) |
1254 | { |
1255 | struct mei_bus_message *mei_msg; |
1256 | struct hbm_host_version_response *version_res; |
1257 | struct hbm_props_response *props_res; |
1258 | struct hbm_host_enum_response *enum_res; |
1259 | struct hbm_dma_setup_response *dma_setup_res; |
1260 | struct hbm_add_client_request *add_cl_req; |
1261 | struct hbm_capability_response *capability_res; |
1262 | int ret; |
1263 | |
1264 | struct mei_hbm_cl_cmd *cl_cmd; |
1265 | struct hbm_client_connect_request *disconnect_req; |
1266 | struct hbm_flow_control *fctrl; |
1267 | struct hbm_client_dma_response *client_dma_res; |
1268 | |
1269 | /* read the message to our buffer */ |
1270 | BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf)); |
1271 | mei_read_slots(dev, buf: dev->rd_msg_buf, len: hdr->length); |
1272 | mei_msg = (struct mei_bus_message *)dev->rd_msg_buf; |
1273 | cl_cmd = (struct mei_hbm_cl_cmd *)mei_msg; |
1274 | |
1275 | /* ignore spurious message and prevent reset nesting |
1276 | * hbm is put to idle during system reset |
1277 | */ |
1278 | if (dev->hbm_state == MEI_HBM_IDLE) { |
1279 | dev_dbg(dev->dev, "hbm: state is idle ignore spurious messages\n" ); |
1280 | return 0; |
1281 | } |
1282 | |
1283 | switch (mei_msg->hbm_cmd) { |
1284 | case HOST_START_RES_CMD: |
1285 | dev_dbg(dev->dev, "hbm: start: response message received.\n" ); |
1286 | |
1287 | dev->init_clients_timer = 0; |
1288 | |
1289 | version_res = (struct hbm_host_version_response *)mei_msg; |
1290 | |
1291 | dev_dbg(dev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n" , |
1292 | HBM_MAJOR_VERSION, HBM_MINOR_VERSION, |
1293 | version_res->me_max_version.major_version, |
1294 | version_res->me_max_version.minor_version); |
1295 | |
1296 | if (version_res->host_version_supported) { |
1297 | dev->version.major_version = HBM_MAJOR_VERSION; |
1298 | dev->version.minor_version = HBM_MINOR_VERSION; |
1299 | } else { |
1300 | dev->version.major_version = |
1301 | version_res->me_max_version.major_version; |
1302 | dev->version.minor_version = |
1303 | version_res->me_max_version.minor_version; |
1304 | } |
1305 | |
1306 | if (!mei_hbm_version_is_supported(dev)) { |
1307 | dev_warn(dev->dev, "hbm: start: version mismatch - stopping the driver.\n" ); |
1308 | |
1309 | dev->hbm_state = MEI_HBM_STOPPED; |
1310 | if (mei_hbm_stop_req(dev)) { |
1311 | dev_err(dev->dev, "hbm: start: failed to send stop request\n" ); |
1312 | return -EIO; |
1313 | } |
1314 | break; |
1315 | } |
1316 | |
1317 | mei_hbm_config_features(dev); |
1318 | |
1319 | if (dev->dev_state != MEI_DEV_INIT_CLIENTS || |
1320 | dev->hbm_state != MEI_HBM_STARTING) { |
1321 | if (dev->dev_state == MEI_DEV_POWER_DOWN || |
1322 | dev->dev_state == MEI_DEV_POWERING_DOWN) { |
1323 | dev_dbg(dev->dev, "hbm: start: on shutdown, ignoring\n" ); |
1324 | return 0; |
1325 | } |
1326 | dev_err(dev->dev, "hbm: start: state mismatch, [%d, %d]\n" , |
1327 | dev->dev_state, dev->hbm_state); |
1328 | return -EPROTO; |
1329 | } |
1330 | |
1331 | if (dev->hbm_f_cap_supported) { |
1332 | if (mei_hbm_capabilities_req(dev)) |
1333 | return -EIO; |
1334 | wake_up(&dev->wait_hbm_start); |
1335 | break; |
1336 | } |
1337 | |
1338 | if (dev->hbm_f_dr_supported) { |
1339 | if (mei_dmam_ring_alloc(dev)) |
1340 | dev_info(dev->dev, "running w/o dma ring\n" ); |
1341 | if (mei_dma_ring_is_allocated(dev)) { |
1342 | if (mei_hbm_dma_setup_req(dev)) |
1343 | return -EIO; |
1344 | |
1345 | wake_up(&dev->wait_hbm_start); |
1346 | break; |
1347 | } |
1348 | } |
1349 | |
1350 | dev->hbm_f_dr_supported = 0; |
1351 | mei_dmam_ring_free(dev); |
1352 | |
1353 | if (mei_hbm_enum_clients_req(dev)) |
1354 | return -EIO; |
1355 | |
1356 | wake_up(&dev->wait_hbm_start); |
1357 | break; |
1358 | |
1359 | case MEI_HBM_CAPABILITIES_RES_CMD: |
1360 | dev_dbg(dev->dev, "hbm: capabilities response: message received.\n" ); |
1361 | |
1362 | dev->init_clients_timer = 0; |
1363 | |
1364 | if (dev->dev_state != MEI_DEV_INIT_CLIENTS || |
1365 | dev->hbm_state != MEI_HBM_CAP_SETUP) { |
1366 | if (dev->dev_state == MEI_DEV_POWER_DOWN || |
1367 | dev->dev_state == MEI_DEV_POWERING_DOWN) { |
1368 | dev_dbg(dev->dev, "hbm: capabilities response: on shutdown, ignoring\n" ); |
1369 | return 0; |
1370 | } |
1371 | dev_err(dev->dev, "hbm: capabilities response: state mismatch, [%d, %d]\n" , |
1372 | dev->dev_state, dev->hbm_state); |
1373 | return -EPROTO; |
1374 | } |
1375 | |
1376 | capability_res = (struct hbm_capability_response *)mei_msg; |
1377 | if (!(capability_res->capability_granted[0] & HBM_CAP_VT)) |
1378 | dev->hbm_f_vt_supported = 0; |
1379 | if (!(capability_res->capability_granted[0] & HBM_CAP_CD)) |
1380 | dev->hbm_f_cd_supported = 0; |
1381 | |
1382 | if (!(capability_res->capability_granted[0] & HBM_CAP_GSC)) |
1383 | dev->hbm_f_gsc_supported = 0; |
1384 | |
1385 | if (dev->hbm_f_dr_supported) { |
1386 | if (mei_dmam_ring_alloc(dev)) |
1387 | dev_info(dev->dev, "running w/o dma ring\n" ); |
1388 | if (mei_dma_ring_is_allocated(dev)) { |
1389 | if (mei_hbm_dma_setup_req(dev)) |
1390 | return -EIO; |
1391 | break; |
1392 | } |
1393 | } |
1394 | |
1395 | dev->hbm_f_dr_supported = 0; |
1396 | mei_dmam_ring_free(dev); |
1397 | |
1398 | if (mei_hbm_enum_clients_req(dev)) |
1399 | return -EIO; |
1400 | break; |
1401 | |
1402 | case MEI_HBM_DMA_SETUP_RES_CMD: |
1403 | dev_dbg(dev->dev, "hbm: dma setup response: message received.\n" ); |
1404 | |
1405 | dev->init_clients_timer = 0; |
1406 | |
1407 | if (dev->dev_state != MEI_DEV_INIT_CLIENTS || |
1408 | dev->hbm_state != MEI_HBM_DR_SETUP) { |
1409 | if (dev->dev_state == MEI_DEV_POWER_DOWN || |
1410 | dev->dev_state == MEI_DEV_POWERING_DOWN) { |
1411 | dev_dbg(dev->dev, "hbm: dma setup response: on shutdown, ignoring\n" ); |
1412 | return 0; |
1413 | } |
1414 | dev_err(dev->dev, "hbm: dma setup response: state mismatch, [%d, %d]\n" , |
1415 | dev->dev_state, dev->hbm_state); |
1416 | return -EPROTO; |
1417 | } |
1418 | |
1419 | dma_setup_res = (struct hbm_dma_setup_response *)mei_msg; |
1420 | |
1421 | if (dma_setup_res->status) { |
1422 | u8 status = dma_setup_res->status; |
1423 | |
1424 | if (status == MEI_HBMS_NOT_ALLOWED) { |
1425 | dev_dbg(dev->dev, "hbm: dma setup not allowed\n" ); |
1426 | } else { |
1427 | dev_info(dev->dev, "hbm: dma setup response: failure = %d %s\n" , |
1428 | status, |
1429 | mei_hbm_status_str(status)); |
1430 | } |
1431 | dev->hbm_f_dr_supported = 0; |
1432 | mei_dmam_ring_free(dev); |
1433 | } |
1434 | |
1435 | if (mei_hbm_enum_clients_req(dev)) |
1436 | return -EIO; |
1437 | break; |
1438 | |
1439 | case CLIENT_CONNECT_RES_CMD: |
1440 | dev_dbg(dev->dev, "hbm: client connect response: message received.\n" ); |
1441 | mei_hbm_cl_res(dev, rs: cl_cmd, fop_type: MEI_FOP_CONNECT); |
1442 | break; |
1443 | |
1444 | case CLIENT_DISCONNECT_RES_CMD: |
1445 | dev_dbg(dev->dev, "hbm: client disconnect response: message received.\n" ); |
1446 | mei_hbm_cl_res(dev, rs: cl_cmd, fop_type: MEI_FOP_DISCONNECT); |
1447 | break; |
1448 | |
1449 | case MEI_FLOW_CONTROL_CMD: |
1450 | dev_dbg(dev->dev, "hbm: client flow control response: message received.\n" ); |
1451 | |
1452 | fctrl = (struct hbm_flow_control *)mei_msg; |
1453 | mei_hbm_cl_tx_flow_ctrl_creds_res(dev, fctrl); |
1454 | break; |
1455 | |
1456 | case MEI_PG_ISOLATION_ENTRY_RES_CMD: |
1457 | dev_dbg(dev->dev, "hbm: power gate isolation entry response received\n" ); |
1458 | ret = mei_hbm_pg_enter_res(dev); |
1459 | if (ret) |
1460 | return ret; |
1461 | break; |
1462 | |
1463 | case MEI_PG_ISOLATION_EXIT_REQ_CMD: |
1464 | dev_dbg(dev->dev, "hbm: power gate isolation exit request received\n" ); |
1465 | ret = mei_hbm_pg_exit_res(dev); |
1466 | if (ret) |
1467 | return ret; |
1468 | break; |
1469 | |
1470 | case HOST_CLIENT_PROPERTIES_RES_CMD: |
1471 | dev_dbg(dev->dev, "hbm: properties response: message received.\n" ); |
1472 | |
1473 | dev->init_clients_timer = 0; |
1474 | |
1475 | if (dev->dev_state != MEI_DEV_INIT_CLIENTS || |
1476 | dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) { |
1477 | if (dev->dev_state == MEI_DEV_POWER_DOWN || |
1478 | dev->dev_state == MEI_DEV_POWERING_DOWN) { |
1479 | dev_dbg(dev->dev, "hbm: properties response: on shutdown, ignoring\n" ); |
1480 | return 0; |
1481 | } |
1482 | dev_err(dev->dev, "hbm: properties response: state mismatch, [%d, %d]\n" , |
1483 | dev->dev_state, dev->hbm_state); |
1484 | return -EPROTO; |
1485 | } |
1486 | |
1487 | props_res = (struct hbm_props_response *)mei_msg; |
1488 | |
1489 | if (props_res->status == MEI_HBMS_CLIENT_NOT_FOUND) { |
1490 | dev_dbg(dev->dev, "hbm: properties response: %d CLIENT_NOT_FOUND\n" , |
1491 | props_res->me_addr); |
1492 | } else if (props_res->status) { |
1493 | dev_err(dev->dev, "hbm: properties response: wrong status = %d %s\n" , |
1494 | props_res->status, |
1495 | mei_hbm_status_str(props_res->status)); |
1496 | return -EPROTO; |
1497 | } else { |
1498 | mei_hbm_me_cl_add(dev, res: props_res); |
1499 | } |
1500 | |
1501 | /* request property for the next client */ |
1502 | if (mei_hbm_prop_req(dev, start_idx: props_res->me_addr + 1)) |
1503 | return -EIO; |
1504 | |
1505 | break; |
1506 | |
1507 | case HOST_ENUM_RES_CMD: |
1508 | dev_dbg(dev->dev, "hbm: enumeration response: message received\n" ); |
1509 | |
1510 | dev->init_clients_timer = 0; |
1511 | |
1512 | enum_res = (struct hbm_host_enum_response *) mei_msg; |
1513 | BUILD_BUG_ON(sizeof(dev->me_clients_map) |
1514 | < sizeof(enum_res->valid_addresses)); |
1515 | memcpy(dev->me_clients_map, enum_res->valid_addresses, |
1516 | sizeof(enum_res->valid_addresses)); |
1517 | |
1518 | if (dev->dev_state != MEI_DEV_INIT_CLIENTS || |
1519 | dev->hbm_state != MEI_HBM_ENUM_CLIENTS) { |
1520 | if (dev->dev_state == MEI_DEV_POWER_DOWN || |
1521 | dev->dev_state == MEI_DEV_POWERING_DOWN) { |
1522 | dev_dbg(dev->dev, "hbm: enumeration response: on shutdown, ignoring\n" ); |
1523 | return 0; |
1524 | } |
1525 | dev_err(dev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n" , |
1526 | dev->dev_state, dev->hbm_state); |
1527 | return -EPROTO; |
1528 | } |
1529 | |
1530 | dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES; |
1531 | |
1532 | /* first property request */ |
1533 | if (mei_hbm_prop_req(dev, start_idx: 0)) |
1534 | return -EIO; |
1535 | |
1536 | break; |
1537 | |
1538 | case HOST_STOP_RES_CMD: |
1539 | dev_dbg(dev->dev, "hbm: stop response: message received\n" ); |
1540 | |
1541 | dev->init_clients_timer = 0; |
1542 | |
1543 | if (dev->hbm_state != MEI_HBM_STOPPED) { |
1544 | dev_err(dev->dev, "hbm: stop response: state mismatch, [%d, %d]\n" , |
1545 | dev->dev_state, dev->hbm_state); |
1546 | return -EPROTO; |
1547 | } |
1548 | |
1549 | mei_set_devstate(dev, state: MEI_DEV_POWER_DOWN); |
1550 | dev_info(dev->dev, "hbm: stop response: resetting.\n" ); |
1551 | /* force the reset */ |
1552 | return -EPROTO; |
1553 | |
1554 | case CLIENT_DISCONNECT_REQ_CMD: |
1555 | dev_dbg(dev->dev, "hbm: disconnect request: message received\n" ); |
1556 | |
1557 | disconnect_req = (struct hbm_client_connect_request *)mei_msg; |
1558 | mei_hbm_fw_disconnect_req(dev, disconnect_req); |
1559 | break; |
1560 | |
1561 | case ME_STOP_REQ_CMD: |
1562 | dev_dbg(dev->dev, "hbm: stop request: message received\n" ); |
1563 | dev->hbm_state = MEI_HBM_STOPPED; |
1564 | if (mei_hbm_stop_req(dev)) { |
1565 | dev_err(dev->dev, "hbm: stop request: failed to send stop request\n" ); |
1566 | return -EIO; |
1567 | } |
1568 | break; |
1569 | |
1570 | case MEI_HBM_ADD_CLIENT_REQ_CMD: |
1571 | dev_dbg(dev->dev, "hbm: add client request received\n" ); |
1572 | /* |
1573 | * after the host receives the enum_resp |
1574 | * message clients may be added or removed |
1575 | */ |
1576 | if (dev->hbm_state <= MEI_HBM_ENUM_CLIENTS || |
1577 | dev->hbm_state >= MEI_HBM_STOPPED) { |
1578 | dev_err(dev->dev, "hbm: add client: state mismatch, [%d, %d]\n" , |
1579 | dev->dev_state, dev->hbm_state); |
1580 | return -EPROTO; |
1581 | } |
1582 | add_cl_req = (struct hbm_add_client_request *)mei_msg; |
1583 | ret = mei_hbm_fw_add_cl_req(dev, req: add_cl_req); |
1584 | if (ret) { |
1585 | dev_err(dev->dev, "hbm: add client: failed to send response %d\n" , |
1586 | ret); |
1587 | return -EIO; |
1588 | } |
1589 | dev_dbg(dev->dev, "hbm: add client request processed\n" ); |
1590 | break; |
1591 | |
1592 | case MEI_HBM_NOTIFY_RES_CMD: |
1593 | dev_dbg(dev->dev, "hbm: notify response received\n" ); |
1594 | mei_hbm_cl_res(dev, rs: cl_cmd, fop_type: notify_res_to_fop(cmd: cl_cmd)); |
1595 | break; |
1596 | |
1597 | case MEI_HBM_NOTIFICATION_CMD: |
1598 | dev_dbg(dev->dev, "hbm: notification\n" ); |
1599 | mei_hbm_cl_notify(dev, cmd: cl_cmd); |
1600 | break; |
1601 | |
1602 | case MEI_HBM_CLIENT_DMA_MAP_RES_CMD: |
1603 | dev_dbg(dev->dev, "hbm: client dma map response: message received.\n" ); |
1604 | client_dma_res = (struct hbm_client_dma_response *)mei_msg; |
1605 | mei_hbm_cl_dma_map_res(dev, res: client_dma_res); |
1606 | break; |
1607 | |
1608 | case MEI_HBM_CLIENT_DMA_UNMAP_RES_CMD: |
1609 | dev_dbg(dev->dev, "hbm: client dma unmap response: message received.\n" ); |
1610 | client_dma_res = (struct hbm_client_dma_response *)mei_msg; |
1611 | mei_hbm_cl_dma_unmap_res(dev, res: client_dma_res); |
1612 | break; |
1613 | |
1614 | default: |
1615 | WARN(1, "hbm: wrong command %d\n" , mei_msg->hbm_cmd); |
1616 | return -EPROTO; |
1617 | |
1618 | } |
1619 | return 0; |
1620 | } |
1621 | |
1622 | |