ec62ba95ffccaa3a9283d2136dd099bcddfef010
[dpdk.git] / drivers / net / sfc / sfc_mcdi.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2019-2020 Xilinx, Inc.
4  * Copyright(c) 2016-2019 Solarflare Communications Inc.
5  *
6  * This software was jointly developed between OKTET Labs (under contract
7  * for Solarflare) and Solarflare Communications, Inc.
8  */
9
10 #include <rte_cycles.h>
11
12 #include "efx.h"
13 #include "efx_mcdi.h"
14 #include "efx_regs_mcdi.h"
15
16 #include "sfc.h"
17 #include "sfc_debug.h"
18 #include "sfc_log.h"
19 #include "sfc_ev.h"
20
21 #define SFC_MCDI_POLL_INTERVAL_MIN_US   10              /* 10us in 1us units */
22 #define SFC_MCDI_POLL_INTERVAL_MAX_US   (US_PER_S / 10) /* 100ms in 1us units */
23 #define SFC_MCDI_WATCHDOG_INTERVAL_US   (10 * US_PER_S) /* 10s in 1us units */
24
25 static void
26 sfc_mcdi_timeout(struct sfc_adapter *sa)
27 {
28         sfc_warn(sa, "MC TIMEOUT");
29
30         sfc_panic(sa, "MCDI timeout handling is not implemented\n");
31 }
32
33 static inline boolean_t
34 sfc_mcdi_proxy_event_available(struct sfc_adapter *sa)
35 {
36         struct sfc_mcdi *mcdi = &sa->mcdi;
37
38         mcdi->proxy_handle = 0;
39         mcdi->proxy_result = ETIMEDOUT;
40         sfc_ev_mgmt_qpoll(sa);
41         if (mcdi->proxy_result != ETIMEDOUT)
42                 return B_TRUE;
43
44         return B_FALSE;
45 }
46
47 static void
48 sfc_mcdi_poll(struct sfc_adapter *sa, boolean_t proxy)
49 {
50         efx_nic_t *enp;
51         unsigned int delay_total;
52         unsigned int delay_us;
53         boolean_t aborted __rte_unused;
54
55         delay_total = 0;
56         delay_us = SFC_MCDI_POLL_INTERVAL_MIN_US;
57         enp = sa->nic;
58
59         do {
60                 boolean_t poll_completed;
61
62                 poll_completed = (proxy) ? sfc_mcdi_proxy_event_available(sa) :
63                                            efx_mcdi_request_poll(enp);
64                 if (poll_completed)
65                         return;
66
67                 if (delay_total > SFC_MCDI_WATCHDOG_INTERVAL_US) {
68                         if (!proxy) {
69                                 aborted = efx_mcdi_request_abort(enp);
70                                 SFC_ASSERT(aborted);
71                                 sfc_mcdi_timeout(sa);
72                         }
73
74                         return;
75                 }
76
77                 rte_delay_us(delay_us);
78
79                 delay_total += delay_us;
80
81                 /* Exponentially back off the poll frequency */
82                 RTE_BUILD_BUG_ON(SFC_MCDI_POLL_INTERVAL_MAX_US > UINT_MAX / 2);
83                 delay_us *= 2;
84                 if (delay_us > SFC_MCDI_POLL_INTERVAL_MAX_US)
85                         delay_us = SFC_MCDI_POLL_INTERVAL_MAX_US;
86
87         } while (1);
88 }
89
90 static void
91 sfc_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
92 {
93         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
94         struct sfc_mcdi *mcdi = &sa->mcdi;
95         uint32_t proxy_handle;
96
97         rte_spinlock_lock(&mcdi->lock);
98
99         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
100
101         efx_mcdi_request_start(sa->nic, emrp, B_FALSE);
102         sfc_mcdi_poll(sa, B_FALSE);
103
104         if (efx_mcdi_get_proxy_handle(sa->nic, emrp, &proxy_handle) == 0) {
105                 /*
106                  * Authorization is required for the MCDI request;
107                  * wait for an MCDI proxy response event to bring
108                  * a non-zero proxy handle (should be the same as
109                  * the value obtained above) and operation status
110                  */
111                 sfc_mcdi_poll(sa, B_TRUE);
112
113                 if ((mcdi->proxy_handle != 0) &&
114                     (mcdi->proxy_handle != proxy_handle)) {
115                         sfc_err(sa, "Unexpected MCDI proxy event");
116                         emrp->emr_rc = EFAULT;
117                 } else if (mcdi->proxy_result == 0) {
118                         /*
119                          * Authorization succeeded; re-issue the original
120                          * request and poll for an ordinary MCDI response
121                          */
122                         efx_mcdi_request_start(sa->nic, emrp, B_FALSE);
123                         sfc_mcdi_poll(sa, B_FALSE);
124                 } else {
125                         emrp->emr_rc = mcdi->proxy_result;
126                         sfc_err(sa, "MCDI proxy authorization failed "
127                                     "(handle=%08x, result=%d)",
128                                     proxy_handle, mcdi->proxy_result);
129                 }
130         }
131
132         rte_spinlock_unlock(&mcdi->lock);
133 }
134
135 static void
136 sfc_mcdi_ev_cpl(void *arg)
137 {
138         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
139         struct sfc_mcdi *mcdi __rte_unused;
140
141         mcdi = &sa->mcdi;
142         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
143
144         /* MCDI is polled, completions are not expected */
145         SFC_ASSERT(0);
146 }
147
148 static void
149 sfc_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
150 {
151         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
152
153         sfc_warn(sa, "MC %s",
154             (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) ? "REBOOT" :
155             (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) ? "BADASSERT" : "UNKNOWN");
156
157         sfc_schedule_restart(sa);
158 }
159
160 #define SFC_MCDI_LOG_BUF_SIZE   128
161
162 static size_t
163 sfc_mcdi_do_log(const struct sfc_adapter *sa,
164                 char *buffer, void *data, size_t data_size,
165                 size_t pfxsize, size_t position)
166 {
167         uint32_t *words = data;
168         /* Space separator plus 2 characters per byte */
169         const size_t word_str_space = 1 + 2 * sizeof(*words);
170         size_t i;
171
172         for (i = 0; i < data_size; i += sizeof(*words)) {
173                 if (position + word_str_space >=
174                     SFC_MCDI_LOG_BUF_SIZE) {
175                         /* Flush at SFC_MCDI_LOG_BUF_SIZE with backslash
176                          * at the end which is required by netlogdecode.
177                          */
178                         buffer[position] = '\0';
179                         sfc_log_mcdi(sa, "%s \\", buffer);
180                         /* Preserve prefix for the next log message */
181                         position = pfxsize;
182                 }
183                 position += snprintf(buffer + position,
184                                      SFC_MCDI_LOG_BUF_SIZE - position,
185                                      " %08x", *words);
186                 words++;
187         }
188         return position;
189 }
190
191 static void
192 sfc_mcdi_logger(void *arg, efx_log_msg_t type,
193                 void *header, size_t header_size,
194                 void *data, size_t data_size)
195 {
196         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
197         char buffer[SFC_MCDI_LOG_BUF_SIZE];
198         size_t pfxsize;
199         size_t start;
200
201         /*
202          * Unlike the other cases, MCDI logging implies more onerous work
203          * needed to produce a message. If the dynamic log level prevents
204          * the end result from being printed, the CPU time will be wasted.
205          *
206          * To avoid wasting time, the actual level is examined in advance.
207          */
208         if (rte_log_get_level(sa->mcdi.logtype) < (int)SFC_LOG_LEVEL_MCDI)
209                 return;
210
211         /* The format including prefix added by sfc_log_mcdi() is the format
212          * consumed by the Solarflare netlogdecode tool.
213          */
214         pfxsize = snprintf(buffer, sizeof(buffer), "MCDI RPC %s:",
215                            type == EFX_LOG_MCDI_REQUEST ? "REQ" :
216                            type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???");
217         start = sfc_mcdi_do_log(sa, buffer, header, header_size,
218                                 pfxsize, pfxsize);
219         start = sfc_mcdi_do_log(sa, buffer, data, data_size, pfxsize, start);
220         if (start != pfxsize) {
221                 buffer[start] = '\0';
222                 sfc_log_mcdi(sa, "%s", buffer);
223         }
224 }
225
226 static void
227 sfc_mcdi_ev_proxy_response(void *arg, uint32_t handle, efx_rc_t result)
228 {
229         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
230         struct sfc_mcdi *mcdi = &sa->mcdi;
231
232         mcdi->proxy_handle = handle;
233         mcdi->proxy_result = result;
234 }
235
236 int
237 sfc_mcdi_init(struct sfc_adapter *sa)
238 {
239         struct sfc_mcdi *mcdi;
240         size_t max_msg_size;
241         efx_mcdi_transport_t *emtp;
242         int rc;
243
244         sfc_log_init(sa, "entry");
245
246         mcdi = &sa->mcdi;
247
248         SFC_ASSERT(mcdi->state == SFC_MCDI_UNINITIALIZED);
249
250         rte_spinlock_init(&mcdi->lock);
251
252         mcdi->state = SFC_MCDI_INITIALIZED;
253
254         max_msg_size = sizeof(uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
255         rc = sfc_dma_alloc(sa, "mcdi", 0, max_msg_size, sa->socket_id,
256                            &mcdi->mem);
257         if (rc != 0)
258                 goto fail_dma_alloc;
259
260         mcdi->logtype = sfc_register_logtype(&sa->priv.shared->pci_addr,
261                                              SFC_LOGTYPE_MCDI_STR,
262                                              RTE_LOG_NOTICE);
263
264         emtp = &mcdi->transport;
265         emtp->emt_context = sa;
266         emtp->emt_dma_mem = &mcdi->mem;
267         emtp->emt_execute = sfc_mcdi_execute;
268         emtp->emt_ev_cpl = sfc_mcdi_ev_cpl;
269         emtp->emt_exception = sfc_mcdi_exception;
270         emtp->emt_logger = sfc_mcdi_logger;
271         emtp->emt_ev_proxy_response = sfc_mcdi_ev_proxy_response;
272
273         sfc_log_init(sa, "init MCDI");
274         rc = efx_mcdi_init(sa->nic, emtp);
275         if (rc != 0)
276                 goto fail_mcdi_init;
277
278         return 0;
279
280 fail_mcdi_init:
281         memset(emtp, 0, sizeof(*emtp));
282         sfc_dma_free(sa, &mcdi->mem);
283
284 fail_dma_alloc:
285         mcdi->state = SFC_MCDI_UNINITIALIZED;
286         return rc;
287 }
288
289 void
290 sfc_mcdi_fini(struct sfc_adapter *sa)
291 {
292         struct sfc_mcdi *mcdi;
293         efx_mcdi_transport_t *emtp;
294
295         sfc_log_init(sa, "entry");
296
297         mcdi = &sa->mcdi;
298         emtp = &mcdi->transport;
299
300         rte_spinlock_lock(&mcdi->lock);
301
302         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
303         mcdi->state = SFC_MCDI_UNINITIALIZED;
304
305         sfc_log_init(sa, "fini MCDI");
306         efx_mcdi_fini(sa->nic);
307         memset(emtp, 0, sizeof(*emtp));
308
309         rte_spinlock_unlock(&mcdi->lock);
310
311         sfc_dma_free(sa, &mcdi->mem);
312 }