]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sfxge/common/hunt_mcdi.c
MFH
[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 "efx.h"
35 #include "efx_impl.h"
36
37
38 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
39
40 #if EFSYS_OPT_MCDI
41
42 #ifndef WITH_MCDI_V2
43 #error "WITH_MCDI_V2 required for EF10 MCDIv2 commands."
44 #endif
45
46
47         __checkReturn   efx_rc_t
48 ef10_mcdi_init(
49         __in            efx_nic_t *enp,
50         __in            const efx_mcdi_transport_t *emtp)
51 {
52         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
53         efsys_mem_t *esmp = emtp->emt_dma_mem;
54         efx_dword_t dword;
55         efx_rc_t rc;
56
57         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
58                     enp->en_family == EFX_FAMILY_MEDFORD);
59         EFSYS_ASSERT(enp->en_features & EFX_FEATURE_MCDI_DMA);
60
61         /*
62          * All EF10 firmware supports MCDIv2 and MCDIv1.
63          * Medford BootROM supports MCDIv2 and MCDIv1.
64          * Huntington BootROM supports MCDIv1 only.
65          */
66         emip->emi_max_version = 2;
67
68         /* A host DMA buffer is required for EF10 MCDI */
69         if (esmp == NULL) {
70                 rc = EINVAL;
71                 goto fail1;
72         }
73
74         /*
75          * Ensure that the MC doorbell is in a known state before issuing MCDI
76          * commands. The recovery algorithm requires that the MC command buffer
77          * must be 256 byte aligned. See bug24769.
78          */
79         if ((EFSYS_MEM_ADDR(esmp) & 0xFF) != 0) {
80                 rc = EINVAL;
81                 goto fail2;
82         }
83         EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 1);
84         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
85
86         /* Save initial MC reboot status */
87         (void) ef10_mcdi_poll_reboot(enp);
88
89         /* Start a new epoch (allow fresh MCDI requests to succeed) */
90         efx_mcdi_new_epoch(enp);
91
92         return (0);
93
94 fail2:
95         EFSYS_PROBE(fail2);
96 fail1:
97         EFSYS_PROBE1(fail1, efx_rc_t, rc);
98
99         return (rc);
100 }
101
102                         void
103 ef10_mcdi_fini(
104         __in            efx_nic_t *enp)
105 {
106         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
107
108         emip->emi_new_epoch = B_FALSE;
109 }
110
111                         void
112 ef10_mcdi_send_request(
113         __in            efx_nic_t *enp,
114         __in            void *hdrp,
115         __in            size_t hdr_len,
116         __in            void *sdup,
117         __in            size_t sdu_len)
118 {
119         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
120         efsys_mem_t *esmp = emtp->emt_dma_mem;
121         efx_dword_t dword;
122         unsigned int pos;
123
124         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
125                     enp->en_family == EFX_FAMILY_MEDFORD);
126
127         /* Write the header */
128         for (pos = 0; pos < hdr_len; pos += sizeof (efx_dword_t)) {
129                 dword = *(efx_dword_t *)((uint8_t *)hdrp + pos);
130                 EFSYS_MEM_WRITED(esmp, pos, &dword);
131         }
132
133         /* Write the payload */
134         for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) {
135                 dword = *(efx_dword_t *)((uint8_t *)sdup + pos);
136                 EFSYS_MEM_WRITED(esmp, hdr_len + pos, &dword);
137         }
138
139         /* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */
140         EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, hdr_len + sdu_len);
141         EFSYS_PIO_WRITE_BARRIER();
142
143         /* Ring the doorbell to post the command DMA address to the MC */
144         EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0,
145             EFSYS_MEM_ADDR(esmp) >> 32);
146         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE);
147
148         EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0,
149             EFSYS_MEM_ADDR(esmp) & 0xffffffff);
150         EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
151 }
152
153                         void
154 ef10_mcdi_request_copyout(
155         __in            efx_nic_t *enp,
156         __in            efx_mcdi_req_t *emrp)
157 {
158 #if EFSYS_OPT_MCDI_LOGGING
159         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
160 #endif /* EFSYS_OPT_MCDI_LOGGING */
161         efx_dword_t hdr[2];
162         unsigned int hdr_len;
163         size_t bytes;
164
165         if (emrp->emr_out_buf == NULL)
166                 return;
167
168         /* Read the command header to detect MCDI response format */
169         hdr_len = sizeof (hdr[0]);
170         ef10_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
171         if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
172                 /*
173                  * Read the actual payload length. The length given in the event
174                  * is only correct for responses with the V1 format.
175                  */
176                 ef10_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
177                 hdr_len += sizeof (hdr[1]);
178
179                 emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
180                                             MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
181         }
182
183         /* Copy payload out into caller supplied buffer */
184         bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
185         ef10_mcdi_read_response(enp, emrp->emr_out_buf, hdr_len, bytes);
186
187 #if EFSYS_OPT_MCDI_LOGGING
188         if (emtp->emt_logger != NULL) {
189                 emtp->emt_logger(emtp->emt_context,
190                     EFX_LOG_MCDI_RESPONSE,
191                     &hdr, hdr_len,
192                     emrp->emr_out_buf, bytes);
193         }
194 #endif /* EFSYS_OPT_MCDI_LOGGING */
195 }
196
197         __checkReturn   boolean_t
198 ef10_mcdi_poll_response(
199         __in            efx_nic_t *enp)
200 {
201         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
202         efsys_mem_t *esmp = emtp->emt_dma_mem;
203         efx_dword_t hdr;
204
205         EFSYS_MEM_READD(esmp, 0, &hdr);
206         return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
207 }
208
209                         void
210 ef10_mcdi_read_response(
211         __in                    efx_nic_t *enp,
212         __out_bcount(length)    void *bufferp,
213         __in                    size_t offset,
214         __in                    size_t length)
215 {
216         const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
217         efsys_mem_t *esmp = emtp->emt_dma_mem;
218         unsigned int pos;
219         efx_dword_t data;
220
221         for (pos = 0; pos < length; pos += sizeof (efx_dword_t)) {
222                 EFSYS_MEM_READD(esmp, offset + pos, &data);
223                 memcpy((uint8_t *)bufferp + pos, &data,
224                     MIN(sizeof (data), length - pos));
225         }
226 }
227
228                         efx_rc_t
229 ef10_mcdi_poll_reboot(
230         __in            efx_nic_t *enp)
231 {
232         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
233         efx_dword_t dword;
234         uint32_t old_status;
235         uint32_t new_status;
236         efx_rc_t rc;
237
238         old_status = emip->emi_mc_reboot_status;
239
240         /* Update MC reboot status word */
241         EFX_BAR_TBL_READD(enp, ER_DZ_BIU_MC_SFT_STATUS_REG, 0, &dword, B_FALSE);
242         new_status = dword.ed_u32[0];
243
244         /* MC has rebooted if the value has changed */
245         if (new_status != old_status) {
246                 emip->emi_mc_reboot_status = new_status;
247
248                 /*
249                  * FIXME: Ignore detected MC REBOOT for now.
250                  *
251                  * The Siena support for checking for MC reboot from status
252                  * flags is broken - see comments in siena_mcdi_poll_reboot().
253                  * As the generic MCDI code is shared the EF10 reboot
254                  * detection suffers similar problems.
255                  *
256                  * Do not report an error when the boot status changes until
257                  * this can be handled by common code drivers (and reworked to
258                  * support Siena too).
259                  */
260                 if (B_FALSE) {
261                         rc = EIO;
262                         goto fail1;
263                 }
264         }
265
266         return (0);
267
268 fail1:
269         EFSYS_PROBE1(fail1, efx_rc_t, rc);
270
271         return (rc);
272 }
273
274         __checkReturn   efx_rc_t
275 ef10_mcdi_feature_supported(
276         __in            efx_nic_t *enp,
277         __in            efx_mcdi_feature_id_t id,
278         __out           boolean_t *supportedp)
279 {
280         efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
281         uint32_t privilege_mask = encp->enc_privilege_mask;
282         efx_rc_t rc;
283
284         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
285                     enp->en_family == EFX_FAMILY_MEDFORD);
286
287         /*
288          * Use privilege mask state at MCDI attach.
289          */
290
291         switch (id) {
292         case EFX_MCDI_FEATURE_FW_UPDATE:
293                 /*
294                  * Admin privilege must be used prior to introduction of
295                  * specific flag.
296                  */
297                 *supportedp =
298                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
299                 break;
300         case EFX_MCDI_FEATURE_LINK_CONTROL:
301                 /*
302                  * Admin privilege used prior to introduction of
303                  * specific flag.
304                  */
305                 *supportedp =
306                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, LINK) ||
307                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
308                 break;
309         case EFX_MCDI_FEATURE_MACADDR_CHANGE:
310                 /*
311                  * Admin privilege must be used prior to introduction of
312                  * mac spoofing privilege (at v4.6), which is used up to
313                  * introduction of change mac spoofing privilege (at v4.7)
314                  */
315                 *supportedp =
316                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, CHANGE_MAC) ||
317                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) ||
318                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
319                 break;
320         case EFX_MCDI_FEATURE_MAC_SPOOFING:
321                 /*
322                  * Admin privilege must be used prior to introduction of
323                  * mac spoofing privilege (at v4.6), which is used up to
324                  * introduction of mac spoofing TX privilege (at v4.7)
325                  */
326                 *supportedp =
327                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING_TX) ||
328                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) ||
329                     EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN);
330                 break;
331         default:
332                 rc = ENOTSUP;
333                 goto fail1;
334                 break;
335         }
336
337         return (0);
338
339 fail1:
340         EFSYS_PROBE1(fail1, efx_rc_t, rc);
341
342         return (rc);
343 }
344
345 #endif  /* EFSYS_OPT_MCDI */
346
347 #endif  /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */