]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sfxge/common/hunt_mcdi.c
sfxge: fix interrupt handling for Medford
[FreeBSD/FreeBSD.git] / sys / dev / sfxge / common / hunt_mcdi.c
1 /*-
2  * Copyright (c) 2012-2015 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include "efsys.h"
35 #include "efx.h"
36 #include "efx_impl.h"
37
38
39 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
40
41 #if EFSYS_OPT_MCDI
42
43 #ifndef WITH_MCDI_V2
44 #error "WITH_MCDI_V2 required for EF10 MCDIv2 commands."
45 #endif
46
47 typedef enum efx_mcdi_header_type_e {
48         EFX_MCDI_HEADER_TYPE_V1, /* MCDIv0 (BootROM), MCDIv1 commands */
49         EFX_MCDI_HEADER_TYPE_V2, /* MCDIv2 commands */
50 } efx_mcdi_header_type_t;
51
52 /*
53  * Return the header format to use for sending an MCDI request.
54  *
55  * An MCDIv1 (Siena compatible) command should use MCDIv2 encapsulation if the
56  * request input buffer or response output buffer are too large for the MCDIv1
57  * format. An MCDIv2 command must always be sent using MCDIv2 encapsulation.
58  */
59 #define EFX_MCDI_HEADER_TYPE(_cmd, _length)                             \
60         ((((_cmd) & ~EFX_MASK32(MCDI_HEADER_CODE)) ||                   \
61         ((_length) & ~EFX_MASK32(MCDI_HEADER_DATALEN))) ?               \
62         EFX_MCDI_HEADER_TYPE_V2 : EFX_MCDI_HEADER_TYPE_V1)
63
64
65 /*
66  * MCDI Header NOT_EPOCH flag
67  * ==========================
68  * A new epoch begins at initial startup or after an MC reboot, and defines when
69  * the MC should reject stale MCDI requests.
70  *
71  * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all
72  * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1.
73  *
74  * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a
75  * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0.
76  */
77
78
79         __checkReturn   efx_rc_t
80 ef10_mcdi_init(
81         __in            efx_nic_t *enp,
82         __in            const efx_mcdi_transport_t *emtp)
83 {
84         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
85         efsys_mem_t *esmp = emtp->emt_dma_mem;
86         efx_dword_t dword;
87         efx_rc_t rc;
88
89         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
90                     enp->en_family == EFX_FAMILY_MEDFORD);
91         EFSYS_ASSERT(enp->en_features & EFX_FEATURE_MCDI_DMA);
92
93         /*
94          * All EF10 firmware supports MCDIv2 and MCDIv1.
95          * Medford BootROM supports MCDIv2 and MCDIv1.
96          * Huntington BootROM supports MCDIv1 only.
97          */
98         emip->emi_max_version = 2;
99
100         /* A host DMA buffer is required for EF10 MCDI */
101         if (esmp == NULL) {
102                 rc = EINVAL;
103                 goto fail1;
104         }
105
106         /*
107          * Ensure that the MC doorbell is in a known state before issuing MCDI
108          * commands. The recovery algorithm requires that the MC command buffer
109          * must be 256 byte aligned. See bug24769.
110          */
111         if ((EFSYS_MEM_ADDR(esmp) & 0xFF) != 0) {
112                 rc = EINVAL;
113                 goto fail2;
114         }
115         EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 1);
116         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
117
118         /* Save initial MC reboot status */
119         (void) ef10_mcdi_poll_reboot(enp);
120
121         /* Start a new epoch (allow fresh MCDI requests to succeed) */
122         efx_mcdi_new_epoch(enp);
123
124         return (0);
125
126 fail2:
127         EFSYS_PROBE(fail2);
128 fail1:
129         EFSYS_PROBE1(fail1, efx_rc_t, rc);
130
131         return (rc);
132 }
133
134                         void
135 ef10_mcdi_fini(
136         __in            efx_nic_t *enp)
137 {
138         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
139
140         emip->emi_new_epoch = B_FALSE;
141 }
142
143                         void
144 ef10_mcdi_request_copyin(
145         __in            efx_nic_t *enp,
146         __in            efx_mcdi_req_t *emrp,
147         __in            unsigned int seq,
148         __in            boolean_t ev_cpl,
149         __in            boolean_t new_epoch)
150 {
151         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
152         efsys_mem_t *esmp = emtp->emt_dma_mem;
153         efx_mcdi_header_type_t hdr_type;
154         efx_dword_t dword;
155         efx_dword_t hdr[2];
156         unsigned int xflags;
157         unsigned int pos;
158         size_t offset;
159
160         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
161                     enp->en_family == EFX_FAMILY_MEDFORD);
162
163         xflags = 0;
164         if (ev_cpl)
165                 xflags |= MCDI_HEADER_XFLAGS_EVREQ;
166
167         offset = 0;
168
169         hdr_type = EFX_MCDI_HEADER_TYPE(emrp->emr_cmd,
170             MAX(emrp->emr_in_length, emrp->emr_out_length));
171
172         if (hdr_type == EFX_MCDI_HEADER_TYPE_V2) {
173                 /* Construct MCDI v2 header */
174                 EFX_POPULATE_DWORD_8(hdr[0],
175                     MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
176                     MCDI_HEADER_RESYNC, 1,
177                     MCDI_HEADER_DATALEN, 0,
178                     MCDI_HEADER_SEQ, seq,
179                     MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
180                     MCDI_HEADER_ERROR, 0,
181                     MCDI_HEADER_RESPONSE, 0,
182                     MCDI_HEADER_XFLAGS, xflags);
183                 EFSYS_MEM_WRITED(esmp, offset, &hdr[0]);
184                 offset += sizeof (efx_dword_t);
185
186                 EFX_POPULATE_DWORD_2(hdr[1],
187                     MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
188                     MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
189                 EFSYS_MEM_WRITED(esmp, offset, &hdr[1]);
190                 offset += sizeof (efx_dword_t);
191         } else {
192                 /* Construct MCDI v1 header */
193                 EFX_POPULATE_DWORD_8(hdr[0],
194                     MCDI_HEADER_CODE, emrp->emr_cmd,
195                     MCDI_HEADER_RESYNC, 1,
196                     MCDI_HEADER_DATALEN, emrp->emr_in_length,
197                     MCDI_HEADER_SEQ, seq,
198                     MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
199                     MCDI_HEADER_ERROR, 0,
200                     MCDI_HEADER_RESPONSE, 0,
201                     MCDI_HEADER_XFLAGS, xflags);
202                 EFSYS_MEM_WRITED(esmp, 0, &hdr[0]);
203                 offset += sizeof (efx_dword_t);
204         }
205
206 #if EFSYS_OPT_MCDI_LOGGING
207         if (emtp->emt_logger != NULL) {
208                 emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST,
209                     &hdr, offset,
210                     emrp->emr_in_buf, emrp->emr_in_length);
211         }
212 #endif /* EFSYS_OPT_MCDI_LOGGING */
213
214         /* Construct the payload */
215         for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
216                 memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
217                     MIN(sizeof (dword), emrp->emr_in_length - pos));
218                 EFSYS_MEM_WRITED(esmp, offset + pos, &dword);
219         }
220
221         /* Ring the doorbell to post the command DMA address to the MC */
222         EFSYS_ASSERT((EFSYS_MEM_ADDR(esmp) & 0xFF) == 0);
223
224         /* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */
225         EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, offset + emrp->emr_in_length);
226         EFSYS_PIO_WRITE_BARRIER();
227
228         EFX_POPULATE_DWORD_1(dword,
229             EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) >> 32);
230         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE);
231
232         EFX_POPULATE_DWORD_1(dword,
233             EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) & 0xffffffff);
234         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
235 }
236
237                         void
238 ef10_mcdi_request_copyout(
239         __in            efx_nic_t *enp,
240         __in            efx_mcdi_req_t *emrp)
241 {
242 #if EFSYS_OPT_MCDI_LOGGING
243         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
244 #endif /* EFSYS_OPT_MCDI_LOGGING */
245         efx_dword_t hdr[2];
246         unsigned int hdr_len;
247         size_t bytes;
248
249         if (emrp->emr_out_buf == NULL)
250                 return;
251
252         /* Read the command header to detect MCDI response format */
253         hdr_len = sizeof (hdr[0]);
254         ef10_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
255         if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
256                 /*
257                  * Read the actual payload length. The length given in the event
258                  * is only correct for responses with the V1 format.
259                  */
260                 ef10_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
261                 hdr_len += sizeof (hdr[1]);
262
263                 emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
264                                             MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
265         }
266
267         /* Copy payload out into caller supplied buffer */
268         bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
269         ef10_mcdi_read_response(enp, emrp->emr_out_buf, hdr_len, bytes);
270
271 #if EFSYS_OPT_MCDI_LOGGING
272         if (emtp->emt_logger != NULL) {
273                 emtp->emt_logger(emtp->emt_context,
274                     EFX_LOG_MCDI_RESPONSE,
275                     &hdr, hdr_len,
276                     emrp->emr_out_buf, bytes);
277         }
278 #endif /* EFSYS_OPT_MCDI_LOGGING */
279 }
280
281         __checkReturn   boolean_t
282 ef10_mcdi_poll_response(
283         __in            efx_nic_t *enp)
284 {
285         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
286         efsys_mem_t *esmp = emtp->emt_dma_mem;
287         efx_dword_t hdr;
288
289         EFSYS_MEM_READD(esmp, 0, &hdr);
290         return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
291 }
292
293                         void
294 ef10_mcdi_read_response(
295         __in            efx_nic_t *enp,
296         __out           void *bufferp,
297         __in            size_t offset,
298         __in            size_t length)
299 {
300         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
301         efsys_mem_t *esmp = emtp->emt_dma_mem;
302         unsigned int pos;
303         efx_dword_t data;
304
305         for (pos = 0; pos < length; pos += sizeof (efx_dword_t)) {
306                 EFSYS_MEM_READD(esmp, offset + pos, &data);
307                 memcpy((uint8_t *)bufferp + pos, &data,
308                     MIN(sizeof (data), length - pos));
309         }
310 }
311
312                         efx_rc_t
313 ef10_mcdi_poll_reboot(
314         __in            efx_nic_t *enp)
315 {
316         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
317         efx_dword_t dword;
318         uint32_t old_status;
319         uint32_t new_status;
320         efx_rc_t rc;
321
322         old_status = emip->emi_mc_reboot_status;
323
324         /* Update MC reboot status word */
325         EFX_BAR_TBL_READD(enp, ER_DZ_BIU_MC_SFT_STATUS_REG, 0, &dword, B_FALSE);
326         new_status = dword.ed_u32[0];
327
328         /* MC has rebooted if the value has changed */
329         if (new_status != old_status) {
330                 emip->emi_mc_reboot_status = new_status;
331
332                 /*
333                  * FIXME: Ignore detected MC REBOOT for now.
334                  *
335                  * The Siena support for checking for MC reboot from status
336                  * flags is broken - see comments in siena_mcdi_poll_reboot().
337                  * As the generic MCDI code is shared the EF10 reboot
338                  * detection suffers similar problems.
339                  *
340                  * Do not report an error when the boot status changes until
341                  * this can be handled by common code drivers (and reworked to
342                  * support Siena too).
343                  */
344                 if (B_FALSE) {
345                         rc = EIO;
346                         goto fail1;
347                 }
348         }
349
350         return (0);
351
352 fail1:
353         EFSYS_PROBE1(fail1, efx_rc_t, rc);
354
355         return (rc);
356 }
357
358         __checkReturn   efx_rc_t
359 ef10_mcdi_feature_supported(
360         __in            efx_nic_t *enp,
361         __in            efx_mcdi_feature_id_t id,
362         __out           boolean_t *supportedp)
363 {
364         efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
365         uint32_t privilege_mask = encp->enc_privilege_mask;
366         efx_rc_t rc;
367
368         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
369                     enp->en_family == EFX_FAMILY_MEDFORD);
370
371         /*
372          * Use privilege mask state at MCDI attach.
373          */
374
375         switch (id) {
376         case EFX_MCDI_FEATURE_FW_UPDATE:
377                 /*
378                  * Admin privilege must be used prior to introduction of
379                  * specific flag.
380                  */
381                 *supportedp =
382                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
383                 break;
384         case EFX_MCDI_FEATURE_LINK_CONTROL:
385                 /*
386                  * Admin privilege used prior to introduction of
387                  * specific flag.
388                  */
389                 *supportedp =
390                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, LINK) ||
391                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
392                 break;
393         case EFX_MCDI_FEATURE_MACADDR_CHANGE:
394                 /*
395                  * Admin privilege must be used prior to introduction of
396                  * mac spoofing privilege (at v4.6), which is used up to
397                  * introduction of change mac spoofing privilege (at v4.7)
398                  */
399                 *supportedp =
400                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, CHANGE_MAC) ||
401                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) ||
402                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
403                 break;
404         case EFX_MCDI_FEATURE_MAC_SPOOFING:
405                 /*
406                  * Admin privilege must be used prior to introduction of
407                  * mac spoofing privilege (at v4.6), which is used up to
408                  * introduction of mac spoofing TX privilege (at v4.7)
409                  */
410                 *supportedp =
411                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING_TX) ||
412                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) ||
413                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
414                 break;
415         default:
416                 rc = ENOTSUP;
417                 goto fail1;
418                 break;
419         }
420
421         return (0);
422
423 fail1:
424         EFSYS_PROBE1(fail1, efx_rc_t, rc);
425
426         return (rc);
427 }
428
429 #endif  /* EFSYS_OPT_MCDI */
430
431 #endif  /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */