ef10_mcdi.c revision 291585
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: head/sys/dev/sfxge/common/hunt_mcdi.c 291585 2015-12-01 15:26:46Z arybchik $");
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
47typedef 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
80hunt_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	efx_rc_t 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
117fail2:
118	EFSYS_PROBE(fail2);
119fail1:
120	EFSYS_PROBE1(fail1, efx_rc_t, rc);
121
122	return (rc);
123}
124
125			void
126hunt_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
135hunt_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
219hunt_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
261hunt_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	efx_rc_t 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
361fail3:
362	if (!emrp->emr_quiet)
363		EFSYS_PROBE(fail3);
364fail2:
365	if (!emrp->emr_quiet)
366		EFSYS_PROBE(fail2);
367fail1:
368	if (!emrp->emr_quiet)
369		EFSYS_PROBE1(fail1, efx_rc_t, 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
379out:
380	return (B_TRUE);
381}
382
383			efx_rc_t
384hunt_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	efx_rc_t 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
423fail1:
424	EFSYS_PROBE1(fail1, efx_rc_t, rc);
425
426	return (rc);
427}
428
429	__checkReturn	efx_rc_t
430hunt_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	/*
439	 * Use privilege mask state at MCDI attach.
440	 * Admin privilege must be used prior to introduction of
441	 * specific flag.
442	 */
443	*supportedp = (encp->enc_privilege_mask &
444	    MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN)
445	    == MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN;
446
447	return (0);
448}
449
450	__checkReturn	efx_rc_t
451hunt_mcdi_macaddr_change_supported(
452	__in		efx_nic_t *enp,
453	__out		boolean_t *supportedp)
454{
455	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
456	uint32_t privilege_mask = encp->enc_privilege_mask;
457
458	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
459
460	/*
461	 * Use privilege mask state at MCDI attach.
462	 * Admin privilege must be used prior to introduction of
463	 * specific flag (at v4.6).
464	 */
465	*supportedp =
466	    ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING) ==
467	    MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING) ||
468	    ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN) ==
469	    MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN);
470
471	return (0);
472}
473
474#endif	/* EFSYS_OPT_MCDI */
475
476#endif	/* EFSYS_OPT_HUNTINGTON */
477