]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/sfxge/common/hunt_mcdi.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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
40
41 #if EFSYS_OPT_MCDI
42
43 #ifndef WITH_MCDI_V2
44 #error "WITH_MCDI_V2 required for Huntington 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   int
80 hunt_mcdi_init(
81         __in            efx_nic_t *enp,
82         __in            const efx_mcdi_transport_t *emtp)
83 {
84         efsys_mem_t *esmp = emtp->emt_dma_mem;
85         efx_dword_t dword;
86         int rc;
87
88         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON);
89         EFSYS_ASSERT(enp->en_features & EFX_FEATURE_MCDI_DMA);
90
91         /* A host DMA buffer is required for Huntington MCDI */
92         if (esmp == NULL) {
93                 rc = EINVAL;
94                 goto fail1;
95         }
96
97         /*
98          * Ensure that the MC doorbell is in a known state before issuing MCDI
99          * commands. The recovery algorithm requires that the MC command buffer
100          * must be 256 byte aligned. See bug24769.
101          */
102         if ((EFSYS_MEM_ADDR(esmp) & 0xFF) != 0) {
103                 rc = EINVAL;
104                 goto fail2;
105         }
106         EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 1);
107         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
108
109         /* Save initial MC reboot status */
110         (void) hunt_mcdi_poll_reboot(enp);
111
112         /* Start a new epoch (allow fresh MCDI requests to succeed) */
113         efx_mcdi_new_epoch(enp);
114
115         return (0);
116
117 fail2:
118         EFSYS_PROBE(fail2);
119 fail1:
120         EFSYS_PROBE1(fail1, int, rc);
121
122         return (rc);
123 }
124
125                         void
126 hunt_mcdi_fini(
127         __in            efx_nic_t *enp)
128 {
129         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
130
131         emip->emi_new_epoch = B_FALSE;
132 }
133
134                         void
135 hunt_mcdi_request_copyin(
136         __in            efx_nic_t *enp,
137         __in            efx_mcdi_req_t *emrp,
138         __in            unsigned int seq,
139         __in            boolean_t ev_cpl,
140         __in            boolean_t new_epoch)
141 {
142         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
143         efsys_mem_t *esmp = emtp->emt_dma_mem;
144         efx_mcdi_header_type_t hdr_type;
145         efx_dword_t dword;
146         unsigned int xflags;
147         unsigned int pos;
148         size_t offset;
149
150         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
151
152         xflags = 0;
153         if (ev_cpl)
154                 xflags |= MCDI_HEADER_XFLAGS_EVREQ;
155
156         offset = 0;
157
158         hdr_type = EFX_MCDI_HEADER_TYPE(emrp->emr_cmd,
159             MAX(emrp->emr_in_length, emrp->emr_out_length));
160
161         if (hdr_type == EFX_MCDI_HEADER_TYPE_V2) {
162                 /* Construct MCDI v2 header */
163                 EFX_POPULATE_DWORD_8(dword,
164                     MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
165                     MCDI_HEADER_RESYNC, 1,
166                     MCDI_HEADER_DATALEN, 0,
167                     MCDI_HEADER_SEQ, seq,
168                     MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
169                     MCDI_HEADER_ERROR, 0,
170                     MCDI_HEADER_RESPONSE, 0,
171                     MCDI_HEADER_XFLAGS, xflags);
172                 EFSYS_MEM_WRITED(esmp, offset, &dword);
173                 offset += sizeof (dword);
174
175                 EFX_POPULATE_DWORD_2(dword,
176                     MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
177                     MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
178                 EFSYS_MEM_WRITED(esmp, offset, &dword);
179                 offset += sizeof (dword);
180         } else {
181                 /* Construct MCDI v1 header */
182                 EFX_POPULATE_DWORD_8(dword,
183                     MCDI_HEADER_CODE, emrp->emr_cmd,
184                     MCDI_HEADER_RESYNC, 1,
185                     MCDI_HEADER_DATALEN, emrp->emr_in_length,
186                     MCDI_HEADER_SEQ, seq,
187                     MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
188                     MCDI_HEADER_ERROR, 0,
189                     MCDI_HEADER_RESPONSE, 0,
190                     MCDI_HEADER_XFLAGS, xflags);
191                 EFSYS_MEM_WRITED(esmp, offset, &dword);
192                 offset += sizeof (dword);
193         }
194
195         /* Construct the payload */
196         for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
197                 memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
198                     MIN(sizeof (dword), emrp->emr_in_length - pos));
199                 EFSYS_MEM_WRITED(esmp, offset + pos, &dword);
200         }
201
202         /* Ring the doorbell to post the command DMA address to the MC */
203         EFSYS_ASSERT((EFSYS_MEM_ADDR(esmp) & 0xFF) == 0);
204
205         /* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */
206         EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, offset + emrp->emr_in_length);
207         EFSYS_PIO_WRITE_BARRIER();
208
209         EFX_POPULATE_DWORD_1(dword,
210             EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) >> 32);
211         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE);
212
213         EFX_POPULATE_DWORD_1(dword,
214             EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) & 0xffffffff);
215         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
216 }
217
218                         void
219 hunt_mcdi_request_copyout(
220         __in            efx_nic_t *enp,
221         __in            efx_mcdi_req_t *emrp)
222 {
223         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
224         efsys_mem_t *esmp = emtp->emt_dma_mem;
225         unsigned int pos;
226         unsigned int offset;
227         efx_dword_t hdr;
228         efx_dword_t hdr2;
229         efx_dword_t data;
230         size_t bytes;
231
232         if (emrp->emr_out_buf == NULL)
233                 return;
234
235         /* Read the command header to detect MCDI response format */
236         EFSYS_MEM_READD(esmp, 0, &hdr);
237         if (EFX_DWORD_FIELD(hdr, MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
238                 offset = 2 * sizeof (efx_dword_t);
239
240                 /*
241                  * Read the actual payload length. The length given in the event
242                  * is only correct for responses with the V1 format.
243                  */
244                 EFSYS_MEM_READD(esmp, sizeof (efx_dword_t), &hdr2);
245                 emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr2,
246                                             MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
247         } else {
248                 offset = sizeof (efx_dword_t);
249         }
250
251         /* Copy payload out into caller supplied buffer */
252         bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
253         for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) {
254                 EFSYS_MEM_READD(esmp, offset + pos, &data);
255                 memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data,
256                     MIN(sizeof (data), bytes - pos));
257         }
258 }
259
260         __checkReturn   boolean_t
261 hunt_mcdi_request_poll(
262         __in            efx_nic_t *enp)
263 {
264         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
265         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
266         efsys_mem_t *esmp = emtp->emt_dma_mem;
267         efx_mcdi_req_t *emrp;
268         efx_dword_t dword;
269         unsigned int seq;
270         unsigned int cmd;
271         unsigned int length;
272         size_t offset;
273         int state;
274         int rc;
275
276         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
277
278         /* Serialise against post-watchdog efx_mcdi_ev* */
279         EFSYS_LOCK(enp->en_eslp, state);
280
281         EFSYS_ASSERT(emip->emi_pending_req != NULL);
282         EFSYS_ASSERT(!emip->emi_ev_cpl);
283         emrp = emip->emi_pending_req;
284
285         offset = 0;
286
287         /* Read the command header */
288         EFSYS_MEM_READD(esmp, offset, &dword);
289         offset += sizeof (efx_dword_t);
290         if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) {
291                 EFSYS_UNLOCK(enp->en_eslp, state);
292                 return (B_FALSE);
293         }
294         if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
295                 efx_dword_t dword2;
296
297                 EFSYS_MEM_READD(esmp, offset, &dword2);
298                 offset += sizeof (efx_dword_t);
299
300                 cmd = EFX_DWORD_FIELD(dword2, MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
301                 length = EFX_DWORD_FIELD(dword2, MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
302         } else {
303                 cmd = EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE);
304                 length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
305         }
306
307         /* Request complete */
308         emip->emi_pending_req = NULL;
309         seq = (emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ);
310
311         /* Check for synchronous reboot */
312         if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 && length == 0) {
313                 /* The MC has rebooted since the request was sent. */
314                 EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
315                 hunt_mcdi_poll_reboot(enp);
316
317                 EFSYS_UNLOCK(enp->en_eslp, state);
318                 rc = EIO;
319                 goto fail1;
320         }
321
322         /* Ensure stale MCDI requests fail after an MC reboot. */
323         emip->emi_new_epoch = B_FALSE;
324
325         EFSYS_UNLOCK(enp->en_eslp, state);
326
327         /* Check that the returned data is consistent */
328         if (cmd != emrp->emr_cmd ||
329             EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) {
330                 /* Response is for a different request */
331                 rc = EIO;
332                 goto fail2;
333         }
334         if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
335                 efx_dword_t errdword;
336                 int errcode;
337                 int argnum;
338
339                 /* Read error code (and arg num for MCDI v2 commands) */
340                 EFSYS_MEM_READD(esmp, offset + MC_CMD_ERR_CODE_OFST, &errdword);
341                 errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
342
343                 EFSYS_MEM_READD(esmp, offset + MC_CMD_ERR_ARG_OFST, &errdword);
344                 argnum = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
345
346                 rc = efx_mcdi_request_errcode(errcode);
347                 if (!emrp->emr_quiet) {
348                         EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
349                             int, errcode, int, argnum);
350                 }
351                 goto fail3;
352
353         } else {
354                 emrp->emr_out_length_used = length;
355                 emrp->emr_rc = 0;
356                 hunt_mcdi_request_copyout(enp, emrp);
357         }
358
359         goto out;
360
361 fail3:
362         if (!emrp->emr_quiet)
363                 EFSYS_PROBE(fail3);
364 fail2:
365         if (!emrp->emr_quiet)
366                 EFSYS_PROBE(fail2);
367 fail1:
368         if (!emrp->emr_quiet)
369                 EFSYS_PROBE1(fail1, int, rc);
370
371         /* Fill out error state */
372         emrp->emr_rc = rc;
373         emrp->emr_out_length_used = 0;
374
375         /* Reboot/Assertion */
376         if (rc == EIO || rc == EINTR)
377                 efx_mcdi_raise_exception(enp, emrp, rc);
378
379 out:
380         return (B_TRUE);
381 }
382
383                         int
384 hunt_mcdi_poll_reboot(
385         __in            efx_nic_t *enp)
386 {
387         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
388         efx_dword_t dword;
389         uint32_t old_status;
390         uint32_t new_status;
391         int rc;
392
393         old_status = emip->emi_mc_reboot_status;
394
395         /* Update MC reboot status word */
396         EFX_BAR_TBL_READD(enp, ER_DZ_BIU_MC_SFT_STATUS_REG, 0, &dword, B_FALSE);
397         new_status = dword.ed_u32[0];
398
399         /* MC has rebooted if the value has changed */
400         if (new_status != old_status) {
401                 emip->emi_mc_reboot_status = new_status;
402
403                 /*
404                  * FIXME: Ignore detected MC REBOOT for now.
405                  *
406                  * The Siena support for checking for MC reboot from status
407                  * flags is broken - see comments in siena_mcdi_poll_reboot().
408                  * As the generic MCDI code is shared the Huntington reboot
409                  * detection suffers similar problems.
410                  *
411                  * Do not report an error when the boot status changes until
412                  * this can be handled by common code drivers (and reworked to
413                  * support Siena too).
414                  */
415                 if (B_FALSE) {
416                         rc = EIO;
417                         goto fail1;
418                 }
419         }
420
421         return (0);
422
423 fail1:
424         EFSYS_PROBE1(fail1, int, rc);
425
426         return (rc);
427 }
428
429         __checkReturn   int
430 hunt_mcdi_fw_update_supported(
431         __in            efx_nic_t *enp,
432         __out           boolean_t *supportedp)
433 {
434         efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
435
436         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
437
438         /* use privilege mask state at MCDI attach */
439         *supportedp = (encp->enc_privilege_mask &
440             MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN)
441             == MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN;
442
443         return (0);
444 }
445
446         __checkReturn   int
447 hunt_mcdi_macaddr_change_supported(
448         __in            efx_nic_t *enp,
449         __out           boolean_t *supportedp)
450 {
451         efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
452
453         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
454
455         /* use privilege mask state at MCDI attach */
456         *supportedp = (encp->enc_privilege_mask &
457             MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING)
458             == MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING;
459
460         return (0);
461 }
462
463 #endif  /* EFSYS_OPT_MCDI */
464
465 #endif  /* EFSYS_OPT_HUNTINGTON */