0faad3ed775098ea3d741c009f800e7c41da8f7c
[deb_dpdk.git] / drivers / net / sfc / sfc_mcdi.c
1 /*-
2  *   BSD LICENSE
3  *
4  * Copyright (c) 2016-2017 Solarflare Communications Inc.
5  * All rights reserved.
6  *
7  * This software was jointly developed between OKTET Labs (under contract
8  * for Solarflare) and Solarflare Communications, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  *    this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  *    this list of conditions and the following disclaimer in the documentation
17  *    and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <rte_cycles.h>
33
34 #include "efx.h"
35 #include "efx_mcdi.h"
36 #include "efx_regs_mcdi.h"
37
38 #include "sfc.h"
39 #include "sfc_log.h"
40 #include "sfc_kvargs.h"
41 #include "sfc_ev.h"
42
43 #define SFC_MCDI_POLL_INTERVAL_MIN_US   10              /* 10us in 1us units */
44 #define SFC_MCDI_POLL_INTERVAL_MAX_US   (US_PER_S / 10) /* 100ms in 1us units */
45 #define SFC_MCDI_WATCHDOG_INTERVAL_US   (10 * US_PER_S) /* 10s in 1us units */
46
47 static void
48 sfc_mcdi_timeout(struct sfc_adapter *sa)
49 {
50         sfc_warn(sa, "MC TIMEOUT");
51
52         sfc_panic(sa, "MCDI timeout handling is not implemented\n");
53 }
54
55 static inline boolean_t
56 sfc_mcdi_proxy_event_available(struct sfc_adapter *sa)
57 {
58         struct sfc_mcdi *mcdi = &sa->mcdi;
59
60         mcdi->proxy_handle = 0;
61         mcdi->proxy_result = ETIMEDOUT;
62         sfc_ev_mgmt_qpoll(sa);
63         if (mcdi->proxy_result != ETIMEDOUT)
64                 return B_TRUE;
65
66         return B_FALSE;
67 }
68
69 static void
70 sfc_mcdi_poll(struct sfc_adapter *sa, boolean_t proxy)
71 {
72         efx_nic_t *enp;
73         unsigned int delay_total;
74         unsigned int delay_us;
75         boolean_t aborted __rte_unused;
76
77         delay_total = 0;
78         delay_us = SFC_MCDI_POLL_INTERVAL_MIN_US;
79         enp = sa->nic;
80
81         do {
82                 boolean_t poll_completed;
83
84                 poll_completed = (proxy) ? sfc_mcdi_proxy_event_available(sa) :
85                                            efx_mcdi_request_poll(enp);
86                 if (poll_completed)
87                         return;
88
89                 if (delay_total > SFC_MCDI_WATCHDOG_INTERVAL_US) {
90                         if (!proxy) {
91                                 aborted = efx_mcdi_request_abort(enp);
92                                 SFC_ASSERT(aborted);
93                                 sfc_mcdi_timeout(sa);
94                         }
95
96                         return;
97                 }
98
99                 rte_delay_us(delay_us);
100
101                 delay_total += delay_us;
102
103                 /* Exponentially back off the poll frequency */
104                 RTE_BUILD_BUG_ON(SFC_MCDI_POLL_INTERVAL_MAX_US > UINT_MAX / 2);
105                 delay_us *= 2;
106                 if (delay_us > SFC_MCDI_POLL_INTERVAL_MAX_US)
107                         delay_us = SFC_MCDI_POLL_INTERVAL_MAX_US;
108
109         } while (1);
110 }
111
112 static void
113 sfc_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
114 {
115         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
116         struct sfc_mcdi *mcdi = &sa->mcdi;
117         uint32_t proxy_handle;
118
119         rte_spinlock_lock(&mcdi->lock);
120
121         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
122
123         efx_mcdi_request_start(sa->nic, emrp, B_FALSE);
124         sfc_mcdi_poll(sa, B_FALSE);
125
126         if (efx_mcdi_get_proxy_handle(sa->nic, emrp, &proxy_handle) == 0) {
127                 /*
128                  * Authorization is required for the MCDI request;
129                  * wait for an MCDI proxy response event to bring
130                  * a non-zero proxy handle (should be the same as
131                  * the value obtained above) and operation status
132                  */
133                 sfc_mcdi_poll(sa, B_TRUE);
134
135                 if ((mcdi->proxy_handle != 0) &&
136                     (mcdi->proxy_handle != proxy_handle)) {
137                         sfc_err(sa, "Unexpected MCDI proxy event");
138                         emrp->emr_rc = EFAULT;
139                 } else if (mcdi->proxy_result == 0) {
140                         /*
141                          * Authorization succeeded; re-issue the original
142                          * request and poll for an ordinary MCDI response
143                          */
144                         efx_mcdi_request_start(sa->nic, emrp, B_FALSE);
145                         sfc_mcdi_poll(sa, B_FALSE);
146                 } else {
147                         emrp->emr_rc = mcdi->proxy_result;
148                         sfc_err(sa, "MCDI proxy authorization failed "
149                                     "(handle=%08x, result=%d)",
150                                     proxy_handle, mcdi->proxy_result);
151                 }
152         }
153
154         rte_spinlock_unlock(&mcdi->lock);
155 }
156
157 static void
158 sfc_mcdi_ev_cpl(void *arg)
159 {
160         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
161         struct sfc_mcdi *mcdi __rte_unused;
162
163         mcdi = &sa->mcdi;
164         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
165
166         /* MCDI is polled, completions are not expected */
167         SFC_ASSERT(0);
168 }
169
170 static void
171 sfc_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
172 {
173         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
174
175         sfc_warn(sa, "MC %s",
176             (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) ? "REBOOT" :
177             (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) ? "BADASSERT" : "UNKNOWN");
178
179         sfc_panic(sa, "MCDI exceptions handling is not implemented\n");
180 }
181
182 #define SFC_MCDI_LOG_BUF_SIZE   128
183
184 static size_t
185 sfc_mcdi_do_log(const struct sfc_adapter *sa,
186                 char *buffer, void *data, size_t data_size,
187                 size_t pfxsize, size_t position)
188 {
189         uint32_t *words = data;
190         /* Space separator plus 2 characters per byte */
191         const size_t word_str_space = 1 + 2 * sizeof(*words);
192         size_t i;
193
194         for (i = 0; i < data_size; i += sizeof(*words)) {
195                 if (position + word_str_space >=
196                     SFC_MCDI_LOG_BUF_SIZE) {
197                         /* Flush at SFC_MCDI_LOG_BUF_SIZE with backslash
198                          * at the end which is required by netlogdecode.
199                          */
200                         buffer[position] = '\0';
201                         sfc_info(sa, "%s \\", buffer);
202                         /* Preserve prefix for the next log message */
203                         position = pfxsize;
204                 }
205                 position += snprintf(buffer + position,
206                                      SFC_MCDI_LOG_BUF_SIZE - position,
207                                      " %08x", *words);
208                 words++;
209         }
210         return position;
211 }
212
213 static void
214 sfc_mcdi_logger(void *arg, efx_log_msg_t type,
215                 void *header, size_t header_size,
216                 void *data, size_t data_size)
217 {
218         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
219         char buffer[SFC_MCDI_LOG_BUF_SIZE];
220         size_t pfxsize;
221         size_t start;
222
223         if (!sa->mcdi.logging)
224                 return;
225
226         /* The format including prefix added by sfc_info() is the format
227          * consumed by the Solarflare netlogdecode tool.
228          */
229         pfxsize = snprintf(buffer, sizeof(buffer), "MCDI RPC %s:",
230                            type == EFX_LOG_MCDI_REQUEST ? "REQ" :
231                            type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???");
232         start = sfc_mcdi_do_log(sa, buffer, header, header_size,
233                                 pfxsize, pfxsize);
234         start = sfc_mcdi_do_log(sa, buffer, data, data_size, pfxsize, start);
235         if (start != pfxsize) {
236                 buffer[start] = '\0';
237                 sfc_info(sa, "%s", buffer);
238         }
239 }
240
241 static void
242 sfc_mcdi_ev_proxy_response(void *arg, uint32_t handle, efx_rc_t result)
243 {
244         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
245         struct sfc_mcdi *mcdi = &sa->mcdi;
246
247         mcdi->proxy_handle = handle;
248         mcdi->proxy_result = result;
249 }
250
251 int
252 sfc_mcdi_init(struct sfc_adapter *sa)
253 {
254         struct sfc_mcdi *mcdi;
255         size_t max_msg_size;
256         efx_mcdi_transport_t *emtp;
257         int rc;
258
259         sfc_log_init(sa, "entry");
260
261         mcdi = &sa->mcdi;
262
263         SFC_ASSERT(mcdi->state == SFC_MCDI_UNINITIALIZED);
264
265         rte_spinlock_init(&mcdi->lock);
266
267         mcdi->state = SFC_MCDI_INITIALIZED;
268
269         max_msg_size = sizeof(uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
270         rc = sfc_dma_alloc(sa, "mcdi", 0, max_msg_size, sa->socket_id,
271                            &mcdi->mem);
272         if (rc != 0)
273                 goto fail_dma_alloc;
274
275         /* Convert negative error to positive used in the driver */
276         rc = sfc_kvargs_process(sa, SFC_KVARG_MCDI_LOGGING,
277                                 sfc_kvarg_bool_handler, &mcdi->logging);
278         if (rc != 0)
279                 goto fail_kvargs_process;
280
281         emtp = &mcdi->transport;
282         emtp->emt_context = sa;
283         emtp->emt_dma_mem = &mcdi->mem;
284         emtp->emt_execute = sfc_mcdi_execute;
285         emtp->emt_ev_cpl = sfc_mcdi_ev_cpl;
286         emtp->emt_exception = sfc_mcdi_exception;
287         emtp->emt_logger = sfc_mcdi_logger;
288         emtp->emt_ev_proxy_response = sfc_mcdi_ev_proxy_response;
289
290         sfc_log_init(sa, "init MCDI");
291         rc = efx_mcdi_init(sa->nic, emtp);
292         if (rc != 0)
293                 goto fail_mcdi_init;
294
295         return 0;
296
297 fail_mcdi_init:
298         memset(emtp, 0, sizeof(*emtp));
299
300 fail_kvargs_process:
301         sfc_dma_free(sa, &mcdi->mem);
302
303 fail_dma_alloc:
304         mcdi->state = SFC_MCDI_UNINITIALIZED;
305         return rc;
306 }
307
308 void
309 sfc_mcdi_fini(struct sfc_adapter *sa)
310 {
311         struct sfc_mcdi *mcdi;
312         efx_mcdi_transport_t *emtp;
313
314         sfc_log_init(sa, "entry");
315
316         mcdi = &sa->mcdi;
317         emtp = &mcdi->transport;
318
319         rte_spinlock_lock(&mcdi->lock);
320
321         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
322         mcdi->state = SFC_MCDI_UNINITIALIZED;
323
324         sfc_log_init(sa, "fini MCDI");
325         efx_mcdi_fini(sa->nic);
326         memset(emtp, 0, sizeof(*emtp));
327
328         rte_spinlock_unlock(&mcdi->lock);
329
330         sfc_dma_free(sa, &mcdi->mem);
331 }