ef10_mcdi.c revision 291985
1283514Sarybchik/*-
2283514Sarybchik * Copyright (c) 2012-2015 Solarflare Communications Inc.
3283514Sarybchik * All rights reserved.
4283514Sarybchik *
5283514Sarybchik * Redistribution and use in source and binary forms, with or without
6283514Sarybchik * modification, are permitted provided that the following conditions are met:
7283514Sarybchik *
8283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice,
9283514Sarybchik *    this list of conditions and the following disclaimer.
10283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
11283514Sarybchik *    this list of conditions and the following disclaimer in the documentation
12283514Sarybchik *    and/or other materials provided with the distribution.
13283514Sarybchik *
14283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25283514Sarybchik *
26283514Sarybchik * The views and conclusions contained in the software and documentation are
27283514Sarybchik * those of the authors and should not be interpreted as representing official
28283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project.
29283514Sarybchik */
30283514Sarybchik
31283514Sarybchik#include <sys/cdefs.h>
32283514Sarybchik__FBSDID("$FreeBSD: head/sys/dev/sfxge/common/hunt_mcdi.c 291985 2015-12-08 06:25:52Z arybchik $");
33283514Sarybchik
34283514Sarybchik#include "efsys.h"
35283514Sarybchik#include "efx.h"
36283514Sarybchik#include "efx_impl.h"
37283514Sarybchik
38283514Sarybchik
39283514Sarybchik#if EFSYS_OPT_HUNTINGTON
40283514Sarybchik
41283514Sarybchik#if EFSYS_OPT_MCDI
42283514Sarybchik
43283514Sarybchik#ifndef WITH_MCDI_V2
44283514Sarybchik#error "WITH_MCDI_V2 required for Huntington MCDIv2 commands."
45283514Sarybchik#endif
46283514Sarybchik
47283514Sarybchiktypedef enum efx_mcdi_header_type_e {
48283514Sarybchik	EFX_MCDI_HEADER_TYPE_V1, /* MCDIv0 (BootROM), MCDIv1 commands */
49283514Sarybchik	EFX_MCDI_HEADER_TYPE_V2, /* MCDIv2 commands */
50283514Sarybchik} efx_mcdi_header_type_t;
51283514Sarybchik
52283514Sarybchik/*
53283514Sarybchik * Return the header format to use for sending an MCDI request.
54283514Sarybchik *
55283514Sarybchik * An MCDIv1 (Siena compatible) command should use MCDIv2 encapsulation if the
56283514Sarybchik * request input buffer or response output buffer are too large for the MCDIv1
57283514Sarybchik * format. An MCDIv2 command must always be sent using MCDIv2 encapsulation.
58283514Sarybchik */
59283514Sarybchik#define	EFX_MCDI_HEADER_TYPE(_cmd, _length)				\
60283514Sarybchik	((((_cmd) & ~EFX_MASK32(MCDI_HEADER_CODE)) ||			\
61283514Sarybchik	((_length) & ~EFX_MASK32(MCDI_HEADER_DATALEN)))	?		\
62283514Sarybchik	EFX_MCDI_HEADER_TYPE_V2	: EFX_MCDI_HEADER_TYPE_V1)
63283514Sarybchik
64283514Sarybchik
65283514Sarybchik/*
66283514Sarybchik * MCDI Header NOT_EPOCH flag
67283514Sarybchik * ==========================
68283514Sarybchik * A new epoch begins at initial startup or after an MC reboot, and defines when
69283514Sarybchik * the MC should reject stale MCDI requests.
70283514Sarybchik *
71283514Sarybchik * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all
72283514Sarybchik * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1.
73283514Sarybchik *
74283514Sarybchik * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a
75283514Sarybchik * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0.
76283514Sarybchik */
77283514Sarybchik
78283514Sarybchik
79291436Sarybchik	__checkReturn	efx_rc_t
80283514Sarybchikhunt_mcdi_init(
81283514Sarybchik	__in		efx_nic_t *enp,
82283514Sarybchik	__in		const efx_mcdi_transport_t *emtp)
83283514Sarybchik{
84283514Sarybchik	efsys_mem_t *esmp = emtp->emt_dma_mem;
85283514Sarybchik	efx_dword_t dword;
86291436Sarybchik	efx_rc_t rc;
87283514Sarybchik
88283514Sarybchik	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON);
89283514Sarybchik	EFSYS_ASSERT(enp->en_features & EFX_FEATURE_MCDI_DMA);
90283514Sarybchik
91283514Sarybchik	/* A host DMA buffer is required for Huntington MCDI */
92283514Sarybchik	if (esmp == NULL) {
93283514Sarybchik		rc = EINVAL;
94283514Sarybchik		goto fail1;
95283514Sarybchik	}
96283514Sarybchik
97283514Sarybchik	/*
98283514Sarybchik	 * Ensure that the MC doorbell is in a known state before issuing MCDI
99283514Sarybchik	 * commands. The recovery algorithm requires that the MC command buffer
100283514Sarybchik	 * must be 256 byte aligned. See bug24769.
101283514Sarybchik	 */
102283514Sarybchik	if ((EFSYS_MEM_ADDR(esmp) & 0xFF) != 0) {
103283514Sarybchik		rc = EINVAL;
104283514Sarybchik		goto fail2;
105283514Sarybchik	}
106283514Sarybchik	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 1);
107283514Sarybchik	EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
108283514Sarybchik
109283514Sarybchik	/* Save initial MC reboot status */
110283514Sarybchik	(void) hunt_mcdi_poll_reboot(enp);
111283514Sarybchik
112283514Sarybchik	/* Start a new epoch (allow fresh MCDI requests to succeed) */
113283514Sarybchik	efx_mcdi_new_epoch(enp);
114283514Sarybchik
115283514Sarybchik	return (0);
116283514Sarybchik
117283514Sarybchikfail2:
118283514Sarybchik	EFSYS_PROBE(fail2);
119283514Sarybchikfail1:
120291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
121283514Sarybchik
122283514Sarybchik	return (rc);
123283514Sarybchik}
124283514Sarybchik
125283514Sarybchik			void
126283514Sarybchikhunt_mcdi_fini(
127283514Sarybchik	__in		efx_nic_t *enp)
128283514Sarybchik{
129283514Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
130283514Sarybchik
131283514Sarybchik	emip->emi_new_epoch = B_FALSE;
132283514Sarybchik}
133283514Sarybchik
134283514Sarybchik			void
135283514Sarybchikhunt_mcdi_request_copyin(
136283514Sarybchik	__in		efx_nic_t *enp,
137283514Sarybchik	__in		efx_mcdi_req_t *emrp,
138283514Sarybchik	__in		unsigned int seq,
139283514Sarybchik	__in		boolean_t ev_cpl,
140283514Sarybchik	__in		boolean_t new_epoch)
141283514Sarybchik{
142283514Sarybchik	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
143283514Sarybchik	efsys_mem_t *esmp = emtp->emt_dma_mem;
144283514Sarybchik	efx_mcdi_header_type_t hdr_type;
145283514Sarybchik	efx_dword_t dword;
146291677Sarybchik	efx_dword_t hdr[2];
147283514Sarybchik	unsigned int xflags;
148283514Sarybchik	unsigned int pos;
149283514Sarybchik	size_t offset;
150283514Sarybchik
151283514Sarybchik	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
152283514Sarybchik
153283514Sarybchik	xflags = 0;
154283514Sarybchik	if (ev_cpl)
155283514Sarybchik		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
156283514Sarybchik
157283514Sarybchik	offset = 0;
158283514Sarybchik
159283514Sarybchik	hdr_type = EFX_MCDI_HEADER_TYPE(emrp->emr_cmd,
160283514Sarybchik	    MAX(emrp->emr_in_length, emrp->emr_out_length));
161283514Sarybchik
162283514Sarybchik	if (hdr_type == EFX_MCDI_HEADER_TYPE_V2) {
163283514Sarybchik		/* Construct MCDI v2 header */
164291677Sarybchik		EFX_POPULATE_DWORD_8(hdr[0],
165283514Sarybchik		    MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
166283514Sarybchik		    MCDI_HEADER_RESYNC, 1,
167283514Sarybchik		    MCDI_HEADER_DATALEN, 0,
168283514Sarybchik		    MCDI_HEADER_SEQ, seq,
169283514Sarybchik		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
170283514Sarybchik		    MCDI_HEADER_ERROR, 0,
171283514Sarybchik		    MCDI_HEADER_RESPONSE, 0,
172283514Sarybchik		    MCDI_HEADER_XFLAGS, xflags);
173291677Sarybchik		EFSYS_MEM_WRITED(esmp, offset, &hdr[0]);
174291677Sarybchik		offset += sizeof (efx_dword_t);
175283514Sarybchik
176291677Sarybchik		EFX_POPULATE_DWORD_2(hdr[1],
177283514Sarybchik		    MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
178283514Sarybchik		    MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
179291677Sarybchik		EFSYS_MEM_WRITED(esmp, offset, &hdr[1]);
180291677Sarybchik		offset += sizeof (efx_dword_t);
181283514Sarybchik	} else {
182283514Sarybchik		/* Construct MCDI v1 header */
183291677Sarybchik		EFX_POPULATE_DWORD_8(hdr[0],
184283514Sarybchik		    MCDI_HEADER_CODE, emrp->emr_cmd,
185283514Sarybchik		    MCDI_HEADER_RESYNC, 1,
186283514Sarybchik		    MCDI_HEADER_DATALEN, emrp->emr_in_length,
187283514Sarybchik		    MCDI_HEADER_SEQ, seq,
188283514Sarybchik		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
189283514Sarybchik		    MCDI_HEADER_ERROR, 0,
190283514Sarybchik		    MCDI_HEADER_RESPONSE, 0,
191283514Sarybchik		    MCDI_HEADER_XFLAGS, xflags);
192291677Sarybchik		EFSYS_MEM_WRITED(esmp, 0, &hdr[0]);
193291677Sarybchik		offset += sizeof (efx_dword_t);
194283514Sarybchik	}
195283514Sarybchik
196291677Sarybchik#if EFSYS_OPT_MCDI_LOGGING
197291677Sarybchik	if (emtp->emt_logger != NULL) {
198291677Sarybchik		emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST,
199291677Sarybchik		    &hdr, offset,
200291677Sarybchik		    emrp->emr_in_buf, emrp->emr_in_length);
201291677Sarybchik	}
202291677Sarybchik#endif /* EFSYS_OPT_MCDI_LOGGING */
203291677Sarybchik
204283514Sarybchik	/* Construct the payload */
205283514Sarybchik	for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
206283514Sarybchik		memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
207283514Sarybchik		    MIN(sizeof (dword), emrp->emr_in_length - pos));
208283514Sarybchik		EFSYS_MEM_WRITED(esmp, offset + pos, &dword);
209283514Sarybchik	}
210283514Sarybchik
211283514Sarybchik	/* Ring the doorbell to post the command DMA address to the MC */
212283514Sarybchik	EFSYS_ASSERT((EFSYS_MEM_ADDR(esmp) & 0xFF) == 0);
213283514Sarybchik
214283514Sarybchik	/* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */
215283514Sarybchik	EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, offset + emrp->emr_in_length);
216283514Sarybchik	EFSYS_PIO_WRITE_BARRIER();
217283514Sarybchik
218283514Sarybchik	EFX_POPULATE_DWORD_1(dword,
219283514Sarybchik	    EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) >> 32);
220283514Sarybchik	EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE);
221283514Sarybchik
222283514Sarybchik	EFX_POPULATE_DWORD_1(dword,
223283514Sarybchik	    EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) & 0xffffffff);
224283514Sarybchik	EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE);
225283514Sarybchik}
226283514Sarybchik
227283514Sarybchik			void
228283514Sarybchikhunt_mcdi_request_copyout(
229283514Sarybchik	__in		efx_nic_t *enp,
230283514Sarybchik	__in		efx_mcdi_req_t *emrp)
231283514Sarybchik{
232291985Sarybchik#if EFSYS_OPT_MCDI_LOGGING
233283514Sarybchik	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
234291985Sarybchik#endif /* EFSYS_OPT_MCDI_LOGGING */
235291677Sarybchik	efx_dword_t hdr[2];
236291985Sarybchik	unsigned int hdr_len;
237283514Sarybchik	size_t bytes;
238283514Sarybchik
239283514Sarybchik	if (emrp->emr_out_buf == NULL)
240283514Sarybchik		return;
241283514Sarybchik
242283514Sarybchik	/* Read the command header to detect MCDI response format */
243291985Sarybchik	hdr_len = sizeof (hdr[0]);
244291985Sarybchik	hunt_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
245291677Sarybchik	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
246283514Sarybchik		/*
247283514Sarybchik		 * Read the actual payload length. The length given in the event
248283514Sarybchik		 * is only correct for responses with the V1 format.
249283514Sarybchik		 */
250291985Sarybchik		hunt_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
251291985Sarybchik		hdr_len += sizeof (hdr[1]);
252291985Sarybchik
253291677Sarybchik		emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
254283514Sarybchik					    MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
255283514Sarybchik	}
256283514Sarybchik
257283514Sarybchik	/* Copy payload out into caller supplied buffer */
258283514Sarybchik	bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
259291985Sarybchik	hunt_mcdi_read_response(enp, emrp->emr_out_buf, hdr_len, bytes);
260291677Sarybchik
261291677Sarybchik#if EFSYS_OPT_MCDI_LOGGING
262291677Sarybchik	if (emtp->emt_logger != NULL) {
263291677Sarybchik		emtp->emt_logger(emtp->emt_context,
264291677Sarybchik		    EFX_LOG_MCDI_RESPONSE,
265291985Sarybchik		    &hdr, hdr_len,
266291985Sarybchik		    emrp->emr_out_buf, bytes);
267291677Sarybchik	}
268291677Sarybchik#endif /* EFSYS_OPT_MCDI_LOGGING */
269283514Sarybchik}
270283514Sarybchik
271291928Sarybchikstatic	__checkReturn	boolean_t
272291928Sarybchikhunt_mcdi_poll_response(
273291928Sarybchik	__in		efx_nic_t *enp)
274291928Sarybchik{
275291928Sarybchik	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
276291928Sarybchik	efsys_mem_t *esmp = emtp->emt_dma_mem;
277291928Sarybchik	efx_dword_t hdr;
278291928Sarybchik
279291928Sarybchik	EFSYS_MEM_READD(esmp, 0, &hdr);
280291928Sarybchik	return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
281291928Sarybchik}
282291928Sarybchik
283291985Sarybchik			void
284291985Sarybchikhunt_mcdi_read_response(
285291985Sarybchik	__in		efx_nic_t *enp,
286291985Sarybchik	__out		void *bufferp,
287291985Sarybchik	__in		size_t offset,
288291985Sarybchik	__in		size_t length)
289291985Sarybchik{
290291985Sarybchik	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
291291985Sarybchik	efsys_mem_t *esmp = emtp->emt_dma_mem;
292291985Sarybchik	unsigned int pos;
293291985Sarybchik	efx_dword_t data;
294291985Sarybchik
295291985Sarybchik	for (pos = 0; pos < length; pos += sizeof (efx_dword_t)) {
296291985Sarybchik		EFSYS_MEM_READD(esmp, offset + pos, &data);
297291985Sarybchik		memcpy((uint8_t *)bufferp + pos, &data,
298291985Sarybchik		    MIN(sizeof (data), length - pos));
299291985Sarybchik	}
300291985Sarybchik}
301291985Sarybchik
302283514Sarybchik	__checkReturn	boolean_t
303283514Sarybchikhunt_mcdi_request_poll(
304283514Sarybchik	__in		efx_nic_t *enp)
305283514Sarybchik{
306291985Sarybchik#if EFSYS_OPT_MCDI_LOGGING
307291985Sarybchik	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
308291985Sarybchik#endif /* EFSYS_OPT_MCDI_LOGGING */
309283514Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
310283514Sarybchik	efx_mcdi_req_t *emrp;
311291677Sarybchik	efx_dword_t hdr[2];
312291985Sarybchik	unsigned int hdr_len;
313291985Sarybchik	unsigned int data_len;
314283514Sarybchik	unsigned int seq;
315283514Sarybchik	unsigned int cmd;
316283514Sarybchik	int state;
317291436Sarybchik	efx_rc_t rc;
318283514Sarybchik
319283514Sarybchik	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
320283514Sarybchik
321283514Sarybchik	/* Serialise against post-watchdog efx_mcdi_ev* */
322283514Sarybchik	EFSYS_LOCK(enp->en_eslp, state);
323283514Sarybchik
324283514Sarybchik	EFSYS_ASSERT(emip->emi_pending_req != NULL);
325283514Sarybchik	EFSYS_ASSERT(!emip->emi_ev_cpl);
326283514Sarybchik	emrp = emip->emi_pending_req;
327283514Sarybchik
328291928Sarybchik	/* Check if a response is available */
329291928Sarybchik	if (hunt_mcdi_poll_response(enp) == B_FALSE) {
330283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
331283514Sarybchik		return (B_FALSE);
332283514Sarybchik	}
333291928Sarybchik
334291928Sarybchik	/* Read the response header */
335291985Sarybchik	hdr_len = sizeof (hdr[0]);
336291985Sarybchik	hunt_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
337291985Sarybchik
338291677Sarybchik	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
339291985Sarybchik		hunt_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
340291985Sarybchik		hdr_len += sizeof (hdr[1]);
341283514Sarybchik
342291677Sarybchik		cmd = EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
343291985Sarybchik		data_len =
344291985Sarybchik		    EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
345283514Sarybchik	} else {
346291677Sarybchik		cmd = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE);
347291985Sarybchik		data_len = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_DATALEN);
348283514Sarybchik	}
349283514Sarybchik
350283514Sarybchik	/* Request complete */
351283514Sarybchik	emip->emi_pending_req = NULL;
352283514Sarybchik	seq = (emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ);
353283514Sarybchik
354283514Sarybchik	/* Check for synchronous reboot */
355291985Sarybchik	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR) != 0 && data_len == 0) {
356283514Sarybchik		/* The MC has rebooted since the request was sent. */
357283514Sarybchik		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
358283514Sarybchik		hunt_mcdi_poll_reboot(enp);
359283514Sarybchik
360283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
361283514Sarybchik		rc = EIO;
362283514Sarybchik		goto fail1;
363283514Sarybchik	}
364283514Sarybchik
365283514Sarybchik	/* Ensure stale MCDI requests fail after an MC reboot. */
366283514Sarybchik	emip->emi_new_epoch = B_FALSE;
367283514Sarybchik
368283514Sarybchik	EFSYS_UNLOCK(enp->en_eslp, state);
369283514Sarybchik
370283514Sarybchik	/* Check that the returned data is consistent */
371283514Sarybchik	if (cmd != emrp->emr_cmd ||
372291677Sarybchik	    EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_SEQ) != seq) {
373283514Sarybchik		/* Response is for a different request */
374283514Sarybchik		rc = EIO;
375283514Sarybchik		goto fail2;
376283514Sarybchik	}
377291677Sarybchik	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR)) {
378291677Sarybchik		efx_dword_t err[2];
379291985Sarybchik		unsigned int err_len = MIN(data_len, sizeof (err));
380291985Sarybchik		int err_code = MC_CMD_ERR_EPROTO;
381291985Sarybchik		int err_arg = 0;
382283514Sarybchik
383283514Sarybchik		/* Read error code (and arg num for MCDI v2 commands) */
384291985Sarybchik		hunt_mcdi_read_response(enp, &err[0], hdr_len, err_len);
385283514Sarybchik
386291985Sarybchik		if (err_len >= MC_CMD_ERR_CODE_OFST + sizeof (efx_dword_t))
387291985Sarybchik			err_code = EFX_DWORD_FIELD(err[0], EFX_DWORD_0);
388283514Sarybchik
389291985Sarybchik		if (err_len >= MC_CMD_ERR_ARG_OFST + sizeof (efx_dword_t))
390291985Sarybchik			err_arg = EFX_DWORD_FIELD(err[1], EFX_DWORD_0);
391291985Sarybchik
392291677Sarybchik#if EFSYS_OPT_MCDI_LOGGING
393291677Sarybchik		if (emtp->emt_logger != NULL) {
394291677Sarybchik			emtp->emt_logger(emtp->emt_context,
395291677Sarybchik			    EFX_LOG_MCDI_RESPONSE,
396291985Sarybchik			    &hdr, hdr_len,
397291985Sarybchik			    &err, err_len);
398291677Sarybchik		}
399291677Sarybchik#endif /* EFSYS_OPT_MCDI_LOGGING */
400291677Sarybchik
401291985Sarybchik		rc = efx_mcdi_request_errcode(err_code);
402283514Sarybchik		if (!emrp->emr_quiet) {
403283514Sarybchik			EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
404291985Sarybchik			    int, err_code, int, err_arg);
405283514Sarybchik		}
406283514Sarybchik		goto fail3;
407283514Sarybchik
408283514Sarybchik	} else {
409291985Sarybchik		emrp->emr_out_length_used = data_len;
410283514Sarybchik		emrp->emr_rc = 0;
411283514Sarybchik		hunt_mcdi_request_copyout(enp, emrp);
412283514Sarybchik	}
413283514Sarybchik
414283514Sarybchik	goto out;
415283514Sarybchik
416283514Sarybchikfail3:
417283514Sarybchik	if (!emrp->emr_quiet)
418283514Sarybchik		EFSYS_PROBE(fail3);
419283514Sarybchikfail2:
420283514Sarybchik	if (!emrp->emr_quiet)
421283514Sarybchik		EFSYS_PROBE(fail2);
422283514Sarybchikfail1:
423283514Sarybchik	if (!emrp->emr_quiet)
424291436Sarybchik		EFSYS_PROBE1(fail1, efx_rc_t, rc);
425283514Sarybchik
426283514Sarybchik	/* Fill out error state */
427283514Sarybchik	emrp->emr_rc = rc;
428283514Sarybchik	emrp->emr_out_length_used = 0;
429283514Sarybchik
430283514Sarybchik	/* Reboot/Assertion */
431283514Sarybchik	if (rc == EIO || rc == EINTR)
432283514Sarybchik		efx_mcdi_raise_exception(enp, emrp, rc);
433283514Sarybchik
434283514Sarybchikout:
435283514Sarybchik	return (B_TRUE);
436283514Sarybchik}
437283514Sarybchik
438291436Sarybchik			efx_rc_t
439283514Sarybchikhunt_mcdi_poll_reboot(
440283514Sarybchik	__in		efx_nic_t *enp)
441283514Sarybchik{
442283514Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
443283514Sarybchik	efx_dword_t dword;
444283514Sarybchik	uint32_t old_status;
445283514Sarybchik	uint32_t new_status;
446291436Sarybchik	efx_rc_t rc;
447283514Sarybchik
448283514Sarybchik	old_status = emip->emi_mc_reboot_status;
449283514Sarybchik
450283514Sarybchik	/* Update MC reboot status word */
451283514Sarybchik	EFX_BAR_TBL_READD(enp, ER_DZ_BIU_MC_SFT_STATUS_REG, 0, &dword, B_FALSE);
452283514Sarybchik	new_status = dword.ed_u32[0];
453283514Sarybchik
454283514Sarybchik	/* MC has rebooted if the value has changed */
455283514Sarybchik	if (new_status != old_status) {
456283514Sarybchik		emip->emi_mc_reboot_status = new_status;
457283514Sarybchik
458283514Sarybchik		/*
459283514Sarybchik		 * FIXME: Ignore detected MC REBOOT for now.
460283514Sarybchik		 *
461283514Sarybchik		 * The Siena support for checking for MC reboot from status
462283514Sarybchik		 * flags is broken - see comments in siena_mcdi_poll_reboot().
463283514Sarybchik		 * As the generic MCDI code is shared the Huntington reboot
464283514Sarybchik		 * detection suffers similar problems.
465283514Sarybchik		 *
466283514Sarybchik		 * Do not report an error when the boot status changes until
467283514Sarybchik		 * this can be handled by common code drivers (and reworked to
468283514Sarybchik		 * support Siena too).
469283514Sarybchik		 */
470283514Sarybchik		if (B_FALSE) {
471283514Sarybchik			rc = EIO;
472283514Sarybchik			goto fail1;
473283514Sarybchik		}
474283514Sarybchik	}
475283514Sarybchik
476283514Sarybchik	return (0);
477283514Sarybchik
478283514Sarybchikfail1:
479291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
480283514Sarybchik
481283514Sarybchik	return (rc);
482283514Sarybchik}
483283514Sarybchik
484291436Sarybchik	__checkReturn	efx_rc_t
485283514Sarybchikhunt_mcdi_fw_update_supported(
486283514Sarybchik	__in		efx_nic_t *enp,
487283514Sarybchik	__out		boolean_t *supportedp)
488283514Sarybchik{
489283514Sarybchik	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
490283514Sarybchik
491283514Sarybchik	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
492283514Sarybchik
493291585Sarybchik	/*
494291585Sarybchik	 * Use privilege mask state at MCDI attach.
495291585Sarybchik	 * Admin privilege must be used prior to introduction of
496291585Sarybchik	 * specific flag.
497291585Sarybchik	 */
498283514Sarybchik	*supportedp = (encp->enc_privilege_mask &
499283514Sarybchik	    MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN)
500283514Sarybchik	    == MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN;
501283514Sarybchik
502283514Sarybchik	return (0);
503283514Sarybchik}
504283514Sarybchik
505291436Sarybchik	__checkReturn	efx_rc_t
506283514Sarybchikhunt_mcdi_macaddr_change_supported(
507283514Sarybchik	__in		efx_nic_t *enp,
508283514Sarybchik	__out		boolean_t *supportedp)
509283514Sarybchik{
510283514Sarybchik	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
511291585Sarybchik	uint32_t privilege_mask = encp->enc_privilege_mask;
512283514Sarybchik
513283514Sarybchik	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
514283514Sarybchik
515291585Sarybchik	/*
516291585Sarybchik	 * Use privilege mask state at MCDI attach.
517291585Sarybchik	 * Admin privilege must be used prior to introduction of
518291585Sarybchik	 * specific flag (at v4.6).
519291585Sarybchik	 */
520291585Sarybchik	*supportedp =
521291585Sarybchik	    ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING) ==
522291585Sarybchik	    MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING) ||
523291585Sarybchik	    ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN) ==
524291585Sarybchik	    MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN);
525283514Sarybchik
526283514Sarybchik	return (0);
527283514Sarybchik}
528283514Sarybchik
529291588Sarybchik	__checkReturn	efx_rc_t
530291588Sarybchikhunt_mcdi_link_control_supported(
531291588Sarybchik	__in		efx_nic_t *enp,
532291588Sarybchik	__out		boolean_t *supportedp)
533291588Sarybchik{
534291588Sarybchik	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
535291588Sarybchik	uint32_t privilege_mask = encp->enc_privilege_mask;
536291588Sarybchik
537291588Sarybchik	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON);
538291588Sarybchik
539291588Sarybchik	/*
540291588Sarybchik	 * Use privilege mask state at MCDI attach.
541291588Sarybchik	 * Admin privilege used prior to introduction of
542291588Sarybchik	 * specific flag.
543291588Sarybchik	 */
544291588Sarybchik	*supportedp =
545291588Sarybchik	    ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_LINK) ==
546291588Sarybchik	    MC_CMD_PRIVILEGE_MASK_IN_GRP_LINK) ||
547291588Sarybchik	    ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN) ==
548291588Sarybchik	    MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN);
549291588Sarybchik
550291588Sarybchik	return (0);
551291588Sarybchik}
552291588Sarybchik
553283514Sarybchik#endif	/* EFSYS_OPT_MCDI */
554283514Sarybchik
555283514Sarybchik#endif	/* EFSYS_OPT_HUNTINGTON */
556