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