xref: /linux/drivers/cdx/controller/mcdi.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Management-Controller-to-Driver Interface
4  *
5  * Copyright 2008-2013 Solarflare Communications Inc.
6  * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
7  */
8 #include <linux/delay.h>
9 #include <linux/slab.h>
10 #include <linux/io.h>
11 #include <linux/spinlock.h>
12 #include <linux/netdevice.h>
13 #include <linux/etherdevice.h>
14 #include <linux/ethtool.h>
15 #include <linux/if_vlan.h>
16 #include <linux/timer.h>
17 #include <linux/list.h>
18 #include <linux/pci.h>
19 #include <linux/device.h>
20 #include <linux/rwsem.h>
21 #include <linux/vmalloc.h>
22 #include <net/netevent.h>
23 #include <linux/log2.h>
24 #include <linux/net_tstamp.h>
25 #include <linux/wait.h>
26 
27 #include "bitfield.h"
28 #include "mcdi.h"
29 
30 static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd);
31 static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx);
32 static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
33 				       struct cdx_mcdi_cmd *cmd,
34 				       unsigned int *handle);
35 static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
36 				    bool allow_retry);
37 static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
38 					struct cdx_mcdi_cmd *cmd);
39 static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
40 				  struct cdx_mcdi_cmd *cmd,
41 				  struct cdx_dword *outbuf,
42 				  int len,
43 				  struct list_head *cleanup_list);
44 static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
45 				 struct cdx_mcdi_cmd *cmd,
46 				 struct list_head *cleanup_list);
47 static void cdx_mcdi_cmd_work(struct work_struct *context);
48 static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list);
49 static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
50 				    size_t inlen, int raw, int arg, int err_no);
51 
cdx_cmd_cancelled(struct cdx_mcdi_cmd * cmd)52 static bool cdx_cmd_cancelled(struct cdx_mcdi_cmd *cmd)
53 {
54 	return cmd->state == MCDI_STATE_RUNNING_CANCELLED;
55 }
56 
cdx_mcdi_cmd_release(struct kref * ref)57 static void cdx_mcdi_cmd_release(struct kref *ref)
58 {
59 	kfree(container_of(ref, struct cdx_mcdi_cmd, ref));
60 }
61 
cdx_mcdi_cmd_handle(struct cdx_mcdi_cmd * cmd)62 static unsigned int cdx_mcdi_cmd_handle(struct cdx_mcdi_cmd *cmd)
63 {
64 	return cmd->handle;
65 }
66 
_cdx_mcdi_remove_cmd(struct cdx_mcdi_iface * mcdi,struct cdx_mcdi_cmd * cmd,struct list_head * cleanup_list)67 static void _cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
68 				 struct cdx_mcdi_cmd *cmd,
69 				 struct list_head *cleanup_list)
70 {
71 	/* if cancelled, the completers have already been called */
72 	if (cdx_cmd_cancelled(cmd))
73 		return;
74 
75 	if (cmd->completer) {
76 		list_add_tail(&cmd->cleanup_list, cleanup_list);
77 		++mcdi->outstanding_cleanups;
78 		kref_get(&cmd->ref);
79 	}
80 }
81 
cdx_mcdi_remove_cmd(struct cdx_mcdi_iface * mcdi,struct cdx_mcdi_cmd * cmd,struct list_head * cleanup_list)82 static void cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
83 				struct cdx_mcdi_cmd *cmd,
84 				struct list_head *cleanup_list)
85 {
86 	list_del(&cmd->list);
87 	_cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
88 	cmd->state = MCDI_STATE_FINISHED;
89 	kref_put(&cmd->ref, cdx_mcdi_cmd_release);
90 	if (list_empty(&mcdi->cmd_list))
91 		wake_up(&mcdi->cmd_complete_wq);
92 }
93 
cdx_mcdi_rpc_timeout(struct cdx_mcdi * cdx,unsigned int cmd)94 static unsigned long cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd)
95 {
96 	if (!cdx->mcdi_ops->mcdi_rpc_timeout)
97 		return MCDI_RPC_TIMEOUT;
98 	else
99 		return cdx->mcdi_ops->mcdi_rpc_timeout(cdx, cmd);
100 }
101 
cdx_mcdi_init(struct cdx_mcdi * cdx)102 int cdx_mcdi_init(struct cdx_mcdi *cdx)
103 {
104 	struct cdx_mcdi_iface *mcdi;
105 	int rc = -ENOMEM;
106 
107 	cdx->mcdi = kzalloc(sizeof(*cdx->mcdi), GFP_KERNEL);
108 	if (!cdx->mcdi)
109 		goto fail;
110 
111 	mcdi = cdx_mcdi_if(cdx);
112 	mcdi->cdx = cdx;
113 
114 	mcdi->workqueue = alloc_ordered_workqueue("mcdi_wq", 0);
115 	if (!mcdi->workqueue)
116 		goto fail2;
117 	mutex_init(&mcdi->iface_lock);
118 	mcdi->mode = MCDI_MODE_EVENTS;
119 	INIT_LIST_HEAD(&mcdi->cmd_list);
120 	init_waitqueue_head(&mcdi->cmd_complete_wq);
121 
122 	mcdi->new_epoch = true;
123 
124 	return 0;
125 fail2:
126 	kfree(cdx->mcdi);
127 	cdx->mcdi = NULL;
128 fail:
129 	return rc;
130 }
131 
cdx_mcdi_finish(struct cdx_mcdi * cdx)132 void cdx_mcdi_finish(struct cdx_mcdi *cdx)
133 {
134 	struct cdx_mcdi_iface *mcdi;
135 
136 	mcdi = cdx_mcdi_if(cdx);
137 	if (!mcdi)
138 		return;
139 
140 	cdx_mcdi_wait_for_cleanup(cdx);
141 
142 	destroy_workqueue(mcdi->workqueue);
143 	kfree(cdx->mcdi);
144 	cdx->mcdi = NULL;
145 }
146 
cdx_mcdi_flushed(struct cdx_mcdi_iface * mcdi,bool ignore_cleanups)147 static bool cdx_mcdi_flushed(struct cdx_mcdi_iface *mcdi, bool ignore_cleanups)
148 {
149 	bool flushed;
150 
151 	mutex_lock(&mcdi->iface_lock);
152 	flushed = list_empty(&mcdi->cmd_list) &&
153 		  (ignore_cleanups || !mcdi->outstanding_cleanups);
154 	mutex_unlock(&mcdi->iface_lock);
155 	return flushed;
156 }
157 
158 /* Wait for outstanding MCDI commands to complete. */
cdx_mcdi_wait_for_cleanup(struct cdx_mcdi * cdx)159 static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx)
160 {
161 	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
162 
163 	if (!mcdi)
164 		return;
165 
166 	wait_event(mcdi->cmd_complete_wq,
167 		   cdx_mcdi_flushed(mcdi, false));
168 }
169 
cdx_mcdi_wait_for_quiescence(struct cdx_mcdi * cdx,unsigned int timeout_jiffies)170 int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx,
171 				 unsigned int timeout_jiffies)
172 {
173 	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
174 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
175 	int rc = 0;
176 
177 	if (!mcdi)
178 		return -EINVAL;
179 
180 	flush_workqueue(mcdi->workqueue);
181 
182 	add_wait_queue(&mcdi->cmd_complete_wq, &wait);
183 
184 	while (!cdx_mcdi_flushed(mcdi, true)) {
185 		rc = wait_woken(&wait, TASK_IDLE, timeout_jiffies);
186 		if (rc)
187 			continue;
188 		break;
189 	}
190 
191 	remove_wait_queue(&mcdi->cmd_complete_wq, &wait);
192 
193 	if (rc > 0)
194 		rc = 0;
195 	else if (rc == 0)
196 		rc = -ETIMEDOUT;
197 
198 	return rc;
199 }
200 
cdx_mcdi_payload_csum(const struct cdx_dword * hdr,size_t hdr_len,const struct cdx_dword * sdu,size_t sdu_len)201 static u8 cdx_mcdi_payload_csum(const struct cdx_dword *hdr, size_t hdr_len,
202 				const struct cdx_dword *sdu, size_t sdu_len)
203 {
204 	u8 *p = (u8 *)hdr;
205 	u8 csum = 0;
206 	int i;
207 
208 	for (i = 0; i < hdr_len; i++)
209 		csum += p[i];
210 
211 	p = (u8 *)sdu;
212 	for (i = 0; i < sdu_len; i++)
213 		csum += p[i];
214 
215 	return ~csum & 0xff;
216 }
217 
cdx_mcdi_send_request(struct cdx_mcdi * cdx,struct cdx_mcdi_cmd * cmd)218 static void cdx_mcdi_send_request(struct cdx_mcdi *cdx,
219 				  struct cdx_mcdi_cmd *cmd)
220 {
221 	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
222 	const struct cdx_dword *inbuf = cmd->inbuf;
223 	size_t inlen = cmd->inlen;
224 	struct cdx_dword hdr[2];
225 	size_t hdr_len;
226 	bool not_epoch;
227 	u32 xflags;
228 
229 	if (!mcdi)
230 		return;
231 
232 	mcdi->prev_seq = cmd->seq;
233 	mcdi->seq_held_by[cmd->seq] = cmd;
234 	mcdi->db_held_by = cmd;
235 	cmd->started = jiffies;
236 
237 	not_epoch = !mcdi->new_epoch;
238 	xflags = 0;
239 
240 	/* MCDI v2 */
241 	WARN_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V2);
242 	CDX_POPULATE_DWORD_7(hdr[0],
243 			     MCDI_HEADER_RESPONSE, 0,
244 			     MCDI_HEADER_RESYNC, 1,
245 			     MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
246 			     MCDI_HEADER_DATALEN, 0,
247 			     MCDI_HEADER_SEQ, cmd->seq,
248 			     MCDI_HEADER_XFLAGS, xflags,
249 			     MCDI_HEADER_NOT_EPOCH, not_epoch);
250 	CDX_POPULATE_DWORD_3(hdr[1],
251 			     MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd->cmd,
252 			     MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen,
253 			     MC_CMD_V2_EXTN_IN_MESSAGE_TYPE,
254 			     MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM);
255 	hdr_len = 8;
256 
257 	hdr[0].cdx_u32 |= (__force __le32)(cdx_mcdi_payload_csum(hdr, hdr_len, inbuf, inlen) <<
258 			 MCDI_HEADER_XFLAGS_LBN);
259 
260 	print_hex_dump_debug("MCDI REQ HEADER: ", DUMP_PREFIX_NONE, 32, 4, hdr, hdr_len, false);
261 	print_hex_dump_debug("MCDI REQ PAYLOAD: ", DUMP_PREFIX_NONE, 32, 4, inbuf, inlen, false);
262 
263 	cdx->mcdi_ops->mcdi_request(cdx, hdr, hdr_len, inbuf, inlen);
264 
265 	mcdi->new_epoch = false;
266 }
267 
cdx_mcdi_errno(struct cdx_mcdi * cdx,unsigned int mcdi_err)268 static int cdx_mcdi_errno(struct cdx_mcdi *cdx, unsigned int mcdi_err)
269 {
270 	switch (mcdi_err) {
271 	case 0:
272 	case MC_CMD_ERR_QUEUE_FULL:
273 		return mcdi_err;
274 	case MC_CMD_ERR_EPERM:
275 		return -EPERM;
276 	case MC_CMD_ERR_ENOENT:
277 		return -ENOENT;
278 	case MC_CMD_ERR_EINTR:
279 		return -EINTR;
280 	case MC_CMD_ERR_EAGAIN:
281 		return -EAGAIN;
282 	case MC_CMD_ERR_EACCES:
283 		return -EACCES;
284 	case MC_CMD_ERR_EBUSY:
285 		return -EBUSY;
286 	case MC_CMD_ERR_EINVAL:
287 		return -EINVAL;
288 	case MC_CMD_ERR_ERANGE:
289 		return -ERANGE;
290 	case MC_CMD_ERR_EDEADLK:
291 		return -EDEADLK;
292 	case MC_CMD_ERR_ENOSYS:
293 		return -EOPNOTSUPP;
294 	case MC_CMD_ERR_ETIME:
295 		return -ETIME;
296 	case MC_CMD_ERR_EALREADY:
297 		return -EALREADY;
298 	case MC_CMD_ERR_ENOSPC:
299 		return -ENOSPC;
300 	case MC_CMD_ERR_ENOMEM:
301 		return -ENOMEM;
302 	case MC_CMD_ERR_ENOTSUP:
303 		return -EOPNOTSUPP;
304 	case MC_CMD_ERR_ALLOC_FAIL:
305 		return -ENOBUFS;
306 	case MC_CMD_ERR_MAC_EXIST:
307 		return -EADDRINUSE;
308 	case MC_CMD_ERR_NO_EVB_PORT:
309 		return -EAGAIN;
310 	default:
311 		return -EPROTO;
312 	}
313 }
314 
cdx_mcdi_process_cleanup_list(struct cdx_mcdi * cdx,struct list_head * cleanup_list)315 static void cdx_mcdi_process_cleanup_list(struct cdx_mcdi *cdx,
316 					  struct list_head *cleanup_list)
317 {
318 	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
319 	unsigned int cleanups = 0;
320 
321 	if (!mcdi)
322 		return;
323 
324 	while (!list_empty(cleanup_list)) {
325 		struct cdx_mcdi_cmd *cmd =
326 			list_first_entry(cleanup_list,
327 					 struct cdx_mcdi_cmd, cleanup_list);
328 		cmd->completer(cdx, cmd->cookie, cmd->rc,
329 			       cmd->outbuf, cmd->outlen);
330 		list_del(&cmd->cleanup_list);
331 		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
332 		++cleanups;
333 	}
334 
335 	if (cleanups) {
336 		bool all_done;
337 
338 		mutex_lock(&mcdi->iface_lock);
339 		CDX_WARN_ON_PARANOID(cleanups > mcdi->outstanding_cleanups);
340 		all_done = (mcdi->outstanding_cleanups -= cleanups) == 0;
341 		mutex_unlock(&mcdi->iface_lock);
342 		if (all_done)
343 			wake_up(&mcdi->cmd_complete_wq);
344 	}
345 }
346 
_cdx_mcdi_cancel_cmd(struct cdx_mcdi_iface * mcdi,unsigned int handle,struct list_head * cleanup_list)347 static void _cdx_mcdi_cancel_cmd(struct cdx_mcdi_iface *mcdi,
348 				 unsigned int handle,
349 				 struct list_head *cleanup_list)
350 {
351 	struct cdx_mcdi_cmd *cmd;
352 
353 	list_for_each_entry(cmd, &mcdi->cmd_list, list)
354 		if (cdx_mcdi_cmd_handle(cmd) == handle) {
355 			switch (cmd->state) {
356 			case MCDI_STATE_QUEUED:
357 			case MCDI_STATE_RETRY:
358 				pr_debug("command %#x inlen %zu cancelled in queue\n",
359 					 cmd->cmd, cmd->inlen);
360 				/* if not yet running, properly cancel it */
361 				cmd->rc = -EPIPE;
362 				cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
363 				break;
364 			case MCDI_STATE_RUNNING:
365 			case MCDI_STATE_RUNNING_CANCELLED:
366 			case MCDI_STATE_FINISHED:
367 			default:
368 				/* invalid state? */
369 				WARN_ON(1);
370 			}
371 			break;
372 		}
373 }
374 
cdx_mcdi_cancel_cmd(struct cdx_mcdi * cdx,struct cdx_mcdi_cmd * cmd)375 static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd)
376 {
377 	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
378 	LIST_HEAD(cleanup_list);
379 
380 	if (!mcdi)
381 		return;
382 
383 	mutex_lock(&mcdi->iface_lock);
384 	cdx_mcdi_timeout_cmd(mcdi, cmd, &cleanup_list);
385 	mutex_unlock(&mcdi->iface_lock);
386 	cdx_mcdi_process_cleanup_list(cdx, &cleanup_list);
387 }
388 
389 struct cdx_mcdi_blocking_data {
390 	struct kref ref;
391 	bool done;
392 	wait_queue_head_t wq;
393 	int rc;
394 	struct cdx_dword *outbuf;
395 	size_t outlen;
396 	size_t outlen_actual;
397 };
398 
cdx_mcdi_blocking_data_release(struct kref * ref)399 static void cdx_mcdi_blocking_data_release(struct kref *ref)
400 {
401 	kfree(container_of(ref, struct cdx_mcdi_blocking_data, ref));
402 }
403 
cdx_mcdi_rpc_completer(struct cdx_mcdi * cdx,unsigned long cookie,int rc,struct cdx_dword * outbuf,size_t outlen_actual)404 static void cdx_mcdi_rpc_completer(struct cdx_mcdi *cdx, unsigned long cookie,
405 				   int rc, struct cdx_dword *outbuf,
406 				   size_t outlen_actual)
407 {
408 	struct cdx_mcdi_blocking_data *wait_data =
409 		(struct cdx_mcdi_blocking_data *)cookie;
410 
411 	wait_data->rc = rc;
412 	memcpy(wait_data->outbuf, outbuf,
413 	       min(outlen_actual, wait_data->outlen));
414 	wait_data->outlen_actual = outlen_actual;
415 	/* memory barrier */
416 	smp_wmb();
417 	wait_data->done = true;
418 	wake_up(&wait_data->wq);
419 	kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
420 }
421 
cdx_mcdi_rpc_sync(struct cdx_mcdi * cdx,unsigned int cmd,const struct cdx_dword * inbuf,size_t inlen,struct cdx_dword * outbuf,size_t outlen,size_t * outlen_actual,bool quiet)422 static int cdx_mcdi_rpc_sync(struct cdx_mcdi *cdx, unsigned int cmd,
423 			     const struct cdx_dword *inbuf, size_t inlen,
424 			     struct cdx_dword *outbuf, size_t outlen,
425 			     size_t *outlen_actual, bool quiet)
426 {
427 	struct cdx_mcdi_blocking_data *wait_data;
428 	struct cdx_mcdi_cmd *cmd_item;
429 	unsigned int handle;
430 	int rc;
431 
432 	if (outlen_actual)
433 		*outlen_actual = 0;
434 
435 	wait_data = kmalloc(sizeof(*wait_data), GFP_KERNEL);
436 	if (!wait_data)
437 		return -ENOMEM;
438 
439 	cmd_item = kmalloc(sizeof(*cmd_item), GFP_KERNEL);
440 	if (!cmd_item) {
441 		kfree(wait_data);
442 		return -ENOMEM;
443 	}
444 
445 	kref_init(&wait_data->ref);
446 	wait_data->done = false;
447 	init_waitqueue_head(&wait_data->wq);
448 	wait_data->outbuf = outbuf;
449 	wait_data->outlen = outlen;
450 
451 	kref_init(&cmd_item->ref);
452 	cmd_item->quiet = quiet;
453 	cmd_item->cookie = (unsigned long)wait_data;
454 	cmd_item->completer = &cdx_mcdi_rpc_completer;
455 	cmd_item->cmd = cmd;
456 	cmd_item->inlen = inlen;
457 	cmd_item->inbuf = inbuf;
458 
459 	/* Claim an extra reference for the completer to put. */
460 	kref_get(&wait_data->ref);
461 	rc = cdx_mcdi_rpc_async_internal(cdx, cmd_item, &handle);
462 	if (rc) {
463 		kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
464 		goto out;
465 	}
466 
467 	if (!wait_event_timeout(wait_data->wq, wait_data->done,
468 				cdx_mcdi_rpc_timeout(cdx, cmd)) &&
469 	    !wait_data->done) {
470 		pr_err("MC command 0x%x inlen %zu timed out (sync)\n",
471 		       cmd, inlen);
472 
473 		cdx_mcdi_cancel_cmd(cdx, cmd_item);
474 
475 		wait_data->rc = -ETIMEDOUT;
476 		wait_data->outlen_actual = 0;
477 	}
478 
479 	if (outlen_actual)
480 		*outlen_actual = wait_data->outlen_actual;
481 	rc = wait_data->rc;
482 
483 out:
484 	kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
485 
486 	return rc;
487 }
488 
cdx_mcdi_get_seq(struct cdx_mcdi_iface * mcdi,unsigned char * seq)489 static bool cdx_mcdi_get_seq(struct cdx_mcdi_iface *mcdi, unsigned char *seq)
490 {
491 	*seq = mcdi->prev_seq;
492 	do {
493 		*seq = (*seq + 1) % ARRAY_SIZE(mcdi->seq_held_by);
494 	} while (mcdi->seq_held_by[*seq] && *seq != mcdi->prev_seq);
495 	return !mcdi->seq_held_by[*seq];
496 }
497 
cdx_mcdi_rpc_async_internal(struct cdx_mcdi * cdx,struct cdx_mcdi_cmd * cmd,unsigned int * handle)498 static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
499 				       struct cdx_mcdi_cmd *cmd,
500 				       unsigned int *handle)
501 {
502 	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
503 	LIST_HEAD(cleanup_list);
504 
505 	if (!mcdi) {
506 		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
507 		return -ENETDOWN;
508 	}
509 
510 	if (mcdi->mode == MCDI_MODE_FAIL) {
511 		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
512 		return -ENETDOWN;
513 	}
514 
515 	cmd->mcdi = mcdi;
516 	INIT_WORK(&cmd->work, cdx_mcdi_cmd_work);
517 	INIT_LIST_HEAD(&cmd->list);
518 	INIT_LIST_HEAD(&cmd->cleanup_list);
519 	cmd->rc = 0;
520 	cmd->outbuf = NULL;
521 	cmd->outlen = 0;
522 
523 	queue_work(mcdi->workqueue, &cmd->work);
524 	return 0;
525 }
526 
cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface * mcdi,struct cdx_mcdi_cmd * cmd)527 static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
528 					struct cdx_mcdi_cmd *cmd)
529 {
530 	struct cdx_mcdi *cdx = mcdi->cdx;
531 	u8 seq;
532 
533 	if (!mcdi->db_held_by &&
534 	    cdx_mcdi_get_seq(mcdi, &seq)) {
535 		cmd->seq = seq;
536 		cmd->reboot_seen = false;
537 		cdx_mcdi_send_request(cdx, cmd);
538 		cmd->state = MCDI_STATE_RUNNING;
539 	} else {
540 		cmd->state = MCDI_STATE_QUEUED;
541 	}
542 }
543 
544 /* try to advance other commands */
cdx_mcdi_start_or_queue(struct cdx_mcdi_iface * mcdi,bool allow_retry)545 static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
546 				    bool allow_retry)
547 {
548 	struct cdx_mcdi_cmd *cmd, *tmp;
549 
550 	list_for_each_entry_safe(cmd, tmp, &mcdi->cmd_list, list)
551 		if (cmd->state == MCDI_STATE_QUEUED ||
552 		    (cmd->state == MCDI_STATE_RETRY && allow_retry))
553 			cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
554 }
555 
cdx_mcdi_process_cmd(struct cdx_mcdi * cdx,struct cdx_dword * outbuf,int len)556 void cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len)
557 {
558 	struct cdx_mcdi_iface *mcdi;
559 	struct cdx_mcdi_cmd *cmd;
560 	LIST_HEAD(cleanup_list);
561 	unsigned int respseq;
562 
563 	if (!len || !outbuf) {
564 		pr_err("Got empty MC response\n");
565 		return;
566 	}
567 
568 	mcdi = cdx_mcdi_if(cdx);
569 	if (!mcdi)
570 		return;
571 
572 	respseq = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_SEQ);
573 
574 	mutex_lock(&mcdi->iface_lock);
575 	cmd = mcdi->seq_held_by[respseq];
576 
577 	if (cmd) {
578 		if (cmd->state == MCDI_STATE_FINISHED) {
579 			mutex_unlock(&mcdi->iface_lock);
580 			kref_put(&cmd->ref, cdx_mcdi_cmd_release);
581 			return;
582 		}
583 
584 		cdx_mcdi_complete_cmd(mcdi, cmd, outbuf, len, &cleanup_list);
585 	} else {
586 		pr_err("MC response unexpected for seq : %0X\n", respseq);
587 	}
588 
589 	mutex_unlock(&mcdi->iface_lock);
590 
591 	cdx_mcdi_process_cleanup_list(mcdi->cdx, &cleanup_list);
592 }
593 
cdx_mcdi_cmd_work(struct work_struct * context)594 static void cdx_mcdi_cmd_work(struct work_struct *context)
595 {
596 	struct cdx_mcdi_cmd *cmd =
597 		container_of(context, struct cdx_mcdi_cmd, work);
598 	struct cdx_mcdi_iface *mcdi = cmd->mcdi;
599 
600 	mutex_lock(&mcdi->iface_lock);
601 
602 	cmd->handle = mcdi->prev_handle++;
603 	list_add_tail(&cmd->list, &mcdi->cmd_list);
604 	cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
605 
606 	mutex_unlock(&mcdi->iface_lock);
607 }
608 
609 /*
610  * Returns true if the MCDI module is finished with the command.
611  * (examples of false would be if the command was proxied, or it was
612  * rejected by the MC due to lack of resources and requeued).
613  */
cdx_mcdi_complete_cmd(struct cdx_mcdi_iface * mcdi,struct cdx_mcdi_cmd * cmd,struct cdx_dword * outbuf,int len,struct list_head * cleanup_list)614 static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
615 				  struct cdx_mcdi_cmd *cmd,
616 				  struct cdx_dword *outbuf,
617 				  int len,
618 				  struct list_head *cleanup_list)
619 {
620 	size_t resp_hdr_len, resp_data_len;
621 	struct cdx_mcdi *cdx = mcdi->cdx;
622 	unsigned int respcmd, error;
623 	bool completed = false;
624 	int rc;
625 
626 	/* ensure the command can't go away before this function returns */
627 	kref_get(&cmd->ref);
628 
629 	respcmd = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_CODE);
630 	error = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_ERROR);
631 
632 	if (respcmd != MC_CMD_V2_EXTN) {
633 		resp_hdr_len = 4;
634 		resp_data_len = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_DATALEN);
635 	} else {
636 		resp_data_len = 0;
637 		resp_hdr_len = 8;
638 		if (len >= 8)
639 			resp_data_len =
640 				CDX_DWORD_FIELD(outbuf[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
641 	}
642 
643 	if ((resp_hdr_len + resp_data_len) > len) {
644 		pr_warn("Incomplete MCDI response received %d. Expected %zu\n",
645 			len, (resp_hdr_len + resp_data_len));
646 		resp_data_len = 0;
647 	}
648 
649 	print_hex_dump_debug("MCDI RESP HEADER: ", DUMP_PREFIX_NONE, 32, 4,
650 			     outbuf, resp_hdr_len, false);
651 	print_hex_dump_debug("MCDI RESP PAYLOAD: ", DUMP_PREFIX_NONE, 32, 4,
652 			     outbuf + (resp_hdr_len / 4), resp_data_len, false);
653 
654 	if (error && resp_data_len == 0) {
655 		/* MC rebooted during command */
656 		rc = -EIO;
657 	} else {
658 		if (WARN_ON_ONCE(error && resp_data_len < 4))
659 			resp_data_len = 4;
660 		if (error) {
661 			rc = CDX_DWORD_FIELD(outbuf[resp_hdr_len / 4], CDX_DWORD);
662 			if (!cmd->quiet) {
663 				int err_arg = 0;
664 
665 				if (resp_data_len >= MC_CMD_ERR_ARG_OFST + 4) {
666 					int offset = (resp_hdr_len + MC_CMD_ERR_ARG_OFST) / 4;
667 
668 					err_arg = CDX_DWORD_VAL(outbuf[offset]);
669 				}
670 
671 				_cdx_mcdi_display_error(cdx, cmd->cmd,
672 							cmd->inlen, rc, err_arg,
673 							cdx_mcdi_errno(cdx, rc));
674 			}
675 			rc = cdx_mcdi_errno(cdx, rc);
676 		} else {
677 			rc = 0;
678 		}
679 	}
680 
681 	/* free doorbell */
682 	if (mcdi->db_held_by == cmd)
683 		mcdi->db_held_by = NULL;
684 
685 	if (cdx_cmd_cancelled(cmd)) {
686 		list_del(&cmd->list);
687 		kref_put(&cmd->ref, cdx_mcdi_cmd_release);
688 		completed = true;
689 	} else if (rc == MC_CMD_ERR_QUEUE_FULL) {
690 		cmd->state = MCDI_STATE_RETRY;
691 	} else {
692 		cmd->rc = rc;
693 		cmd->outbuf = outbuf + DIV_ROUND_UP(resp_hdr_len, 4);
694 		cmd->outlen = resp_data_len;
695 		cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
696 		completed = true;
697 	}
698 
699 	/* free sequence number and buffer */
700 	mcdi->seq_held_by[cmd->seq] = NULL;
701 
702 	cdx_mcdi_start_or_queue(mcdi, rc != MC_CMD_ERR_QUEUE_FULL);
703 
704 	/* wake up anyone waiting for flush */
705 	wake_up(&mcdi->cmd_complete_wq);
706 
707 	kref_put(&cmd->ref, cdx_mcdi_cmd_release);
708 
709 	return completed;
710 }
711 
cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface * mcdi,struct cdx_mcdi_cmd * cmd,struct list_head * cleanup_list)712 static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
713 				 struct cdx_mcdi_cmd *cmd,
714 				 struct list_head *cleanup_list)
715 {
716 	struct cdx_mcdi *cdx = mcdi->cdx;
717 
718 	pr_err("MC command 0x%x inlen %zu state %d timed out after %u ms\n",
719 	       cmd->cmd, cmd->inlen, cmd->state,
720 	       jiffies_to_msecs(jiffies - cmd->started));
721 
722 	cmd->rc = -ETIMEDOUT;
723 	cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
724 
725 	cdx_mcdi_mode_fail(cdx, cleanup_list);
726 }
727 
728 /**
729  * cdx_mcdi_rpc - Issue an MCDI command and wait for completion
730  * @cdx: NIC through which to issue the command
731  * @cmd: Command type number
732  * @inbuf: Command parameters
733  * @inlen: Length of command parameters, in bytes. Must be a multiple
734  *	of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
735  * @outbuf: Response buffer. May be %NULL if @outlen is 0.
736  * @outlen: Length of response buffer, in bytes. If the actual
737  *	response is longer than @outlen & ~3, it will be truncated
738  *	to that length.
739  * @outlen_actual: Pointer through which to return the actual response
740  *	length. May be %NULL if this is not needed.
741  *
742  * This function may sleep and therefore must be called in process
743  * context.
744  *
745  * Return: A negative error code, or zero if successful. The error
746  *	code may come from the MCDI response or may indicate a failure
747  *	to communicate with the MC. In the former case, the response
748  *	will still be copied to @outbuf and *@outlen_actual will be
749  *	set accordingly. In the latter case, *@outlen_actual will be
750  *	set to zero.
751  */
cdx_mcdi_rpc(struct cdx_mcdi * cdx,unsigned int cmd,const struct cdx_dword * inbuf,size_t inlen,struct cdx_dword * outbuf,size_t outlen,size_t * outlen_actual)752 int cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd,
753 		 const struct cdx_dword *inbuf, size_t inlen,
754 		 struct cdx_dword *outbuf, size_t outlen,
755 		 size_t *outlen_actual)
756 {
757 	return cdx_mcdi_rpc_sync(cdx, cmd, inbuf, inlen, outbuf, outlen,
758 				 outlen_actual, false);
759 }
760 
761 /**
762  * cdx_mcdi_rpc_async - Schedule an MCDI command to run asynchronously
763  * @cdx: NIC through which to issue the command
764  * @cmd: Command type number
765  * @inbuf: Command parameters
766  * @inlen: Length of command parameters, in bytes
767  * @complete: Function to be called on completion or cancellation.
768  * @cookie: Arbitrary value to be passed to @complete.
769  *
770  * This function does not sleep and therefore may be called in atomic
771  * context.  It will fail if event queues are disabled or if MCDI
772  * event completions have been disabled due to an error.
773  *
774  * If it succeeds, the @complete function will be called exactly once
775  * in process context, when one of the following occurs:
776  * (a) the completion event is received (in process context)
777  * (b) event queues are disabled (in the process that disables them)
778  */
779 int
cdx_mcdi_rpc_async(struct cdx_mcdi * cdx,unsigned int cmd,const struct cdx_dword * inbuf,size_t inlen,cdx_mcdi_async_completer * complete,unsigned long cookie)780 cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd,
781 		   const struct cdx_dword *inbuf, size_t inlen,
782 		   cdx_mcdi_async_completer *complete, unsigned long cookie)
783 {
784 	struct cdx_mcdi_cmd *cmd_item =
785 		kmalloc(sizeof(struct cdx_mcdi_cmd) + inlen, GFP_ATOMIC);
786 
787 	if (!cmd_item)
788 		return -ENOMEM;
789 
790 	kref_init(&cmd_item->ref);
791 	cmd_item->quiet = true;
792 	cmd_item->cookie = cookie;
793 	cmd_item->completer = complete;
794 	cmd_item->cmd = cmd;
795 	cmd_item->inlen = inlen;
796 	/* inbuf is probably not valid after return, so take a copy */
797 	cmd_item->inbuf = (struct cdx_dword *)(cmd_item + 1);
798 	memcpy(cmd_item + 1, inbuf, inlen);
799 
800 	return cdx_mcdi_rpc_async_internal(cdx, cmd_item, NULL);
801 }
802 
_cdx_mcdi_display_error(struct cdx_mcdi * cdx,unsigned int cmd,size_t inlen,int raw,int arg,int err_no)803 static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
804 				    size_t inlen, int raw, int arg, int err_no)
805 {
806 	pr_err("MC command 0x%x inlen %d failed err_no=%d (raw=%d) arg=%d\n",
807 	       cmd, (int)inlen, err_no, raw, arg);
808 }
809 
810 /*
811  * Set MCDI mode to fail to prevent any new commands, then cancel any
812  * outstanding commands.
813  * Caller must hold the mcdi iface_lock.
814  */
cdx_mcdi_mode_fail(struct cdx_mcdi * cdx,struct list_head * cleanup_list)815 static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list)
816 {
817 	struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
818 
819 	if (!mcdi)
820 		return;
821 
822 	mcdi->mode = MCDI_MODE_FAIL;
823 
824 	while (!list_empty(&mcdi->cmd_list)) {
825 		struct cdx_mcdi_cmd *cmd;
826 
827 		cmd = list_first_entry(&mcdi->cmd_list, struct cdx_mcdi_cmd,
828 				       list);
829 		_cdx_mcdi_cancel_cmd(mcdi, cdx_mcdi_cmd_handle(cmd), cleanup_list);
830 	}
831 }
832