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