1227569Sphilip/*-
2227569Sphilip * Copyright 2008-2009 Solarflare Communications Inc.  All rights reserved.
3227569Sphilip *
4227569Sphilip * Redistribution and use in source and binary forms, with or without
5227569Sphilip * modification, are permitted provided that the following conditions
6227569Sphilip * are met:
7227569Sphilip * 1. Redistributions of source code must retain the above copyright
8227569Sphilip *    notice, this list of conditions and the following disclaimer.
9227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright
10227569Sphilip *    notice, this list of conditions and the following disclaimer in the
11227569Sphilip *    documentation and/or other materials provided with the distribution.
12227569Sphilip *
13227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
14227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16227569Sphilip * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23227569Sphilip * SUCH DAMAGE.
24227569Sphilip */
25227569Sphilip
26228078Sphilip#include <sys/cdefs.h>
27228078Sphilip__FBSDID("$FreeBSD$");
28228078Sphilip
29227569Sphilip#include "efsys.h"
30227569Sphilip#include "efx.h"
31227569Sphilip#include "efx_types.h"
32227569Sphilip#include "efx_regs.h"
33227569Sphilip#include "efx_regs_mcdi.h"
34227569Sphilip#include "efx_impl.h"
35227569Sphilip
36227569Sphilip#if EFSYS_OPT_MCDI
37227569Sphilip
38227569Sphilip/* Shared memory layout */
39227569Sphilip
40227569Sphilip#define	MCDI_P1_DBL_OFST	0x0
41227569Sphilip#define	MCDI_P2_DBL_OFST	0x1
42227569Sphilip#define	MCDI_P1_PDU_OFST	0x2
43227569Sphilip#define	MCDI_P2_PDU_OFST	0x42
44227569Sphilip#define	MCDI_P1_REBOOT_OFST	0x1fe
45227569Sphilip#define	MCDI_P2_REBOOT_OFST	0x1ff
46227569Sphilip
47227569Sphilip/* A reboot/assertion causes the MCDI status word to be set after the
48227569Sphilip * command word is set or a REBOOT event is sent. If we notice a reboot
49227569Sphilip * via these mechanisms then wait 10ms for the status word to be set.
50227569Sphilip */
51227569Sphilip#define	MCDI_STATUS_SLEEP_US	10000
52227569Sphilip
53227569Sphilip			void
54227569Sphilipefx_mcdi_request_start(
55227569Sphilip	__in		efx_nic_t *enp,
56227569Sphilip	__in		efx_mcdi_req_t *emrp,
57227569Sphilip	__in		boolean_t ev_cpl)
58227569Sphilip{
59227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
60227569Sphilip	efx_dword_t dword;
61227569Sphilip	unsigned int seq;
62227569Sphilip	unsigned int xflags;
63227569Sphilip	unsigned int pdur;
64227569Sphilip	unsigned int dbr;
65227569Sphilip	unsigned int pos;
66227569Sphilip	int state;
67227569Sphilip
68227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
69227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
70227569Sphilip	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
71227569Sphilip
72227569Sphilip	switch (emip->emi_port)	{
73227569Sphilip	case 1:
74227569Sphilip		pdur = MCDI_P1_PDU_OFST;
75227569Sphilip		dbr = MCDI_P1_DBL_OFST;
76227569Sphilip		break;
77227569Sphilip	case 2:
78227569Sphilip		pdur = MCDI_P2_PDU_OFST;
79227569Sphilip		dbr = MCDI_P2_DBL_OFST;
80227569Sphilip		break;
81227569Sphilip	default:
82227569Sphilip		EFSYS_ASSERT(0);
83227569Sphilip		pdur = dbr = 0;
84227569Sphilip	};
85227569Sphilip
86227569Sphilip	/*
87227569Sphilip	 * efx_mcdi_request_start() is naturally serialised against both
88227569Sphilip	 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
89250460Seadler	 * by virtue of there only being one outstanding MCDI request.
90227569Sphilip	 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
91227569Sphilip	 * at any time, to timeout a pending mcdi request, That request may
92227569Sphilip	 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
93227569Sphilip	 * efx_mcdi_ev_death() may end up running in parallel with
94227569Sphilip	 * efx_mcdi_request_start(). This race is handled by ensuring that
95227569Sphilip	 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
96227569Sphilip	 * en_eslp lock.
97227569Sphilip	 */
98227569Sphilip	EFSYS_LOCK(enp->en_eslp, state);
99227569Sphilip	EFSYS_ASSERT(emip->emi_pending_req == NULL);
100227569Sphilip	emip->emi_pending_req = emrp;
101227569Sphilip	emip->emi_ev_cpl = ev_cpl;
102227569Sphilip	emip->emi_poll_cnt = 0;
103227569Sphilip	seq = emip->emi_seq++ & 0xf;
104227569Sphilip	EFSYS_UNLOCK(enp->en_eslp, state);
105227569Sphilip
106227569Sphilip	xflags = 0;
107227569Sphilip	if (ev_cpl)
108227569Sphilip		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
109227569Sphilip
110227569Sphilip	/* Construct the header in shared memory */
111227569Sphilip	EFX_POPULATE_DWORD_6(dword,
112227569Sphilip			    MCDI_HEADER_CODE, emrp->emr_cmd,
113227569Sphilip			    MCDI_HEADER_RESYNC, 1,
114227569Sphilip			    MCDI_HEADER_DATALEN, emrp->emr_in_length,
115227569Sphilip			    MCDI_HEADER_SEQ, seq,
116227569Sphilip			    MCDI_HEADER_RESPONSE, 0,
117227569Sphilip			    MCDI_HEADER_XFLAGS, xflags);
118227569Sphilip	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
119227569Sphilip
120227569Sphilip	for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
121227569Sphilip		memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
122227569Sphilip		    MIN(sizeof (dword), emrp->emr_in_length - pos));
123227569Sphilip		EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
124227569Sphilip		    pdur + 1 + (pos >> 2), &dword, B_FALSE);
125227569Sphilip	}
126227569Sphilip
127227569Sphilip	/* Ring the doorbell */
128227569Sphilip	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
129227569Sphilip	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
130227569Sphilip}
131227569Sphilip
132227569Sphilipstatic			void
133227569Sphilipefx_mcdi_request_copyout(
134227569Sphilip	__in		efx_nic_t *enp,
135227569Sphilip	__in		efx_mcdi_req_t *emrp)
136227569Sphilip{
137227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
138227569Sphilip	unsigned int pos;
139227569Sphilip	unsigned int pdur;
140227569Sphilip	efx_dword_t data;
141227569Sphilip
142227569Sphilip	pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST;
143227569Sphilip
144227569Sphilip	/* Copy payload out if caller supplied buffer */
145227569Sphilip	if (emrp->emr_out_buf != NULL) {
146227569Sphilip		size_t bytes = MIN(emrp->emr_out_length_used,
147227569Sphilip				    emrp->emr_out_length);
148227569Sphilip		for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) {
149227569Sphilip			EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
150227569Sphilip			    pdur + 1 + (pos >> 2), &data, B_FALSE);
151227569Sphilip			memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data,
152227569Sphilip			    MIN(sizeof (data), bytes - pos));
153227569Sphilip		}
154227569Sphilip	}
155227569Sphilip}
156227569Sphilip
157227569Sphilipstatic			int
158227569Sphilipefx_mcdi_request_errcode(
159227569Sphilip	__in		unsigned int err)
160227569Sphilip{
161227569Sphilip
162227569Sphilip	switch (err) {
163227569Sphilip	case MC_CMD_ERR_ENOENT:
164227569Sphilip		return (ENOENT);
165227569Sphilip	case MC_CMD_ERR_EINTR:
166227569Sphilip		return (EINTR);
167227569Sphilip	case MC_CMD_ERR_EACCES:
168227569Sphilip		return (EACCES);
169227569Sphilip	case MC_CMD_ERR_EBUSY:
170227569Sphilip		return (EBUSY);
171227569Sphilip	case MC_CMD_ERR_EINVAL:
172227569Sphilip		return (EINVAL);
173227569Sphilip	case MC_CMD_ERR_EDEADLK:
174227569Sphilip		return (EDEADLK);
175227569Sphilip	case MC_CMD_ERR_ENOSYS:
176227569Sphilip		return (ENOTSUP);
177227569Sphilip	case MC_CMD_ERR_ETIME:
178227569Sphilip		return (ETIMEDOUT);
179227569Sphilip#ifdef WITH_MCDI_V2
180227569Sphilip	case MC_CMD_ERR_EAGAIN:
181227569Sphilip		return (EAGAIN);
182227569Sphilip	case MC_CMD_ERR_ENOSPC:
183227569Sphilip		return (ENOSPC);
184227569Sphilip#endif
185227569Sphilip	default:
186227569Sphilip		EFSYS_PROBE1(mc_pcol_error, int, err);
187227569Sphilip		return (EIO);
188227569Sphilip	}
189227569Sphilip}
190227569Sphilip
191227569Sphilipstatic			void
192227569Sphilipefx_mcdi_raise_exception(
193227569Sphilip	__in		efx_nic_t *enp,
194227569Sphilip	__in_opt	efx_mcdi_req_t *emrp,
195227569Sphilip	__in		int rc)
196227569Sphilip{
197227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
198227569Sphilip	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
199227569Sphilip	efx_mcdi_exception_t exception;
200227569Sphilip
201227569Sphilip	/* Reboot or Assertion failure only */
202227569Sphilip	EFSYS_ASSERT(rc == EIO || rc == EINTR);
203227569Sphilip
204227569Sphilip	/*
205227569Sphilip	 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
206227569Sphilip	 * then the EIO is not worthy of an exception.
207227569Sphilip	 */
208227569Sphilip	if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
209227569Sphilip		return;
210227569Sphilip
211227569Sphilip	exception = (rc == EIO)
212227569Sphilip		? EFX_MCDI_EXCEPTION_MC_REBOOT
213227569Sphilip		: EFX_MCDI_EXCEPTION_MC_BADASSERT;
214227569Sphilip
215227569Sphilip	emtp->emt_exception(emtp->emt_context, exception);
216227569Sphilip}
217227569Sphilip
218227569Sphilipstatic			int
219227569Sphilipefx_mcdi_poll_reboot(
220227569Sphilip	__in		efx_nic_t *enp)
221227569Sphilip{
222227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
223227569Sphilip	unsigned int rebootr;
224227569Sphilip	efx_dword_t dword;
225227569Sphilip	uint32_t value;
226227569Sphilip
227227569Sphilip	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
228227569Sphilip	rebootr = ((emip->emi_port == 1)
229227569Sphilip	    ? MCDI_P1_REBOOT_OFST
230227569Sphilip	    : MCDI_P2_REBOOT_OFST);
231227569Sphilip
232227569Sphilip	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
233227569Sphilip	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
234227569Sphilip
235227569Sphilip	if (value == 0)
236227569Sphilip		return (0);
237227569Sphilip
238227569Sphilip	EFX_ZERO_DWORD(dword);
239227569Sphilip	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
240227569Sphilip
241227569Sphilip	if (value == MC_STATUS_DWORD_ASSERT)
242227569Sphilip		return (EINTR);
243227569Sphilip	else
244227569Sphilip		return (EIO);
245227569Sphilip}
246227569Sphilip
247227569Sphilip	__checkReturn	boolean_t
248227569Sphilipefx_mcdi_request_poll(
249227569Sphilip	__in		efx_nic_t *enp)
250227569Sphilip{
251227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
252227569Sphilip	efx_mcdi_req_t *emrp;
253227569Sphilip	efx_dword_t dword;
254227569Sphilip	unsigned int pdur;
255227569Sphilip	unsigned int seq;
256227569Sphilip	unsigned int length;
257227569Sphilip	int state;
258227569Sphilip	int rc;
259227569Sphilip
260227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
261227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
262227569Sphilip	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
263227569Sphilip	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
264227569Sphilip
265227569Sphilip	/* Serialise against post-watchdog efx_mcdi_ev* */
266227569Sphilip	EFSYS_LOCK(enp->en_eslp, state);
267227569Sphilip
268227569Sphilip	EFSYS_ASSERT(emip->emi_pending_req != NULL);
269227569Sphilip	EFSYS_ASSERT(!emip->emi_ev_cpl);
270227569Sphilip	emrp = emip->emi_pending_req;
271227569Sphilip
272227569Sphilip	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
273227569Sphilip	if (emip->emi_poll_cnt++ == 0) {
274227569Sphilip		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
275227569Sphilip			emip->emi_pending_req = NULL;
276227569Sphilip			EFSYS_UNLOCK(enp->en_eslp, state);
277227569Sphilip
278227569Sphilip			goto fail1;
279227569Sphilip		}
280227569Sphilip	}
281227569Sphilip
282227569Sphilip	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
283227569Sphilip	pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST;
284227569Sphilip
285227569Sphilip	/* Read the command header */
286227569Sphilip	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE);
287227569Sphilip	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) {
288227569Sphilip		EFSYS_UNLOCK(enp->en_eslp, state);
289227569Sphilip		return (B_FALSE);
290227569Sphilip	}
291227569Sphilip
292227569Sphilip	/* Request complete */
293227569Sphilip	emip->emi_pending_req = NULL;
294227569Sphilip	seq = (emip->emi_seq - 1) & 0xf;
295227569Sphilip
296227569Sphilip	/* Check for synchronous reboot */
297227569Sphilip	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 &&
298227569Sphilip	    EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) {
299227569Sphilip		/* Consume status word */
300227569Sphilip		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
301227569Sphilip		efx_mcdi_poll_reboot(enp);
302227569Sphilip		EFSYS_UNLOCK(enp->en_eslp, state);
303227569Sphilip		rc = EIO;
304227569Sphilip		goto fail2;
305227569Sphilip	}
306227569Sphilip
307227569Sphilip	EFSYS_UNLOCK(enp->en_eslp, state);
308227569Sphilip
309227569Sphilip	/* Check that the returned data is consistent */
310227569Sphilip	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd ||
311227569Sphilip	    EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) {
312227569Sphilip		/* Response is for a different request */
313227569Sphilip		rc = EIO;
314227569Sphilip		goto fail3;
315227569Sphilip	}
316227569Sphilip
317227569Sphilip	length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
318227569Sphilip	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
319227569Sphilip		efx_dword_t errdword;
320227569Sphilip		int errcode;
321227569Sphilip
322227569Sphilip		EFSYS_ASSERT3U(length, ==, 4);
323227569Sphilip		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
324227569Sphilip		    pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2),
325227569Sphilip		    &errdword, B_FALSE);
326227569Sphilip		errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
327227569Sphilip		rc = efx_mcdi_request_errcode(errcode);
328227569Sphilip		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode);
329227569Sphilip		goto fail4;
330227569Sphilip
331227569Sphilip	} else {
332227569Sphilip		emrp->emr_out_length_used = length;
333227569Sphilip		emrp->emr_rc = 0;
334227569Sphilip		efx_mcdi_request_copyout(enp, emrp);
335227569Sphilip	}
336227569Sphilip
337227569Sphilip	goto out;
338227569Sphilip
339227569Sphilipfail4:
340227569Sphilip	EFSYS_PROBE(fail4);
341227569Sphilipfail3:
342227569Sphilip	EFSYS_PROBE(fail3);
343227569Sphilipfail2:
344227569Sphilip	EFSYS_PROBE(fail2);
345227569Sphilipfail1:
346227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
347227569Sphilip
348227569Sphilip	/* Fill out error state */
349227569Sphilip	emrp->emr_rc = rc;
350227569Sphilip	emrp->emr_out_length_used = 0;
351227569Sphilip
352227569Sphilip	/* Reboot/Assertion */
353227569Sphilip	if (rc == EIO || rc == EINTR)
354227569Sphilip		efx_mcdi_raise_exception(enp, emrp, rc);
355227569Sphilip
356227569Sphilipout:
357227569Sphilip	return (B_TRUE);
358227569Sphilip}
359227569Sphilip
360227569Sphilip			void
361227569Sphilipefx_mcdi_execute(
362227569Sphilip	__in		efx_nic_t *enp,
363227569Sphilip	__in		efx_mcdi_req_t *emrp)
364227569Sphilip{
365227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
366227569Sphilip	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
367227569Sphilip
368227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
369227569Sphilip	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
370227569Sphilip	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
371227569Sphilip
372227569Sphilip	emtp->emt_execute(emtp->emt_context, emrp);
373227569Sphilip}
374227569Sphilip
375227569Sphilip			void
376227569Sphilipefx_mcdi_ev_cpl(
377227569Sphilip	__in		efx_nic_t *enp,
378227569Sphilip	__in		unsigned int seq,
379227569Sphilip	__in		unsigned int outlen,
380227569Sphilip	__in		int errcode)
381227569Sphilip{
382227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
383227569Sphilip	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
384227569Sphilip	efx_mcdi_req_t *emrp;
385227569Sphilip	int state;
386227569Sphilip
387227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
388227569Sphilip	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
389227569Sphilip	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
390227569Sphilip
391227569Sphilip	/*
392227569Sphilip	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
393227569Sphilip	 * when we're completing an aborted request.
394227569Sphilip	 */
395227569Sphilip	EFSYS_LOCK(enp->en_eslp, state);
396227569Sphilip	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
397227569Sphilip	    (seq != ((emip->emi_seq - 1) & 0xf))) {
398227569Sphilip		EFSYS_ASSERT(emip->emi_aborted > 0);
399227569Sphilip		if (emip->emi_aborted > 0)
400227569Sphilip			--emip->emi_aborted;
401227569Sphilip		EFSYS_UNLOCK(enp->en_eslp, state);
402227569Sphilip		return;
403227569Sphilip	}
404227569Sphilip
405227569Sphilip	emrp = emip->emi_pending_req;
406227569Sphilip	emip->emi_pending_req = NULL;
407227569Sphilip	EFSYS_UNLOCK(enp->en_eslp, state);
408227569Sphilip
409227569Sphilip	/*
410227569Sphilip	 * Fill out the remaining hdr fields, and copyout the payload
411227569Sphilip	 * if the user supplied an output buffer.
412227569Sphilip	 */
413227569Sphilip	if (errcode != 0) {
414227569Sphilip		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
415227569Sphilip		    int, errcode);
416227569Sphilip		emrp->emr_out_length_used = 0;
417227569Sphilip		emrp->emr_rc = efx_mcdi_request_errcode(errcode);
418227569Sphilip	} else {
419227569Sphilip		emrp->emr_out_length_used = outlen;
420227569Sphilip		emrp->emr_rc = 0;
421227569Sphilip		efx_mcdi_request_copyout(enp, emrp);
422227569Sphilip	}
423227569Sphilip
424227569Sphilip	emtp->emt_ev_cpl(emtp->emt_context);
425227569Sphilip}
426227569Sphilip
427227569Sphilip			void
428227569Sphilipefx_mcdi_ev_death(
429227569Sphilip	__in		efx_nic_t *enp,
430227569Sphilip	__in		int rc)
431227569Sphilip{
432227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
433227569Sphilip	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
434227569Sphilip	efx_mcdi_req_t *emrp = NULL;
435227569Sphilip	boolean_t ev_cpl;
436227569Sphilip	int state;
437227569Sphilip
438227569Sphilip	/*
439227569Sphilip	 * The MCDI request (if there is one) has been terminated, either
440227569Sphilip	 * by a BADASSERT or REBOOT event.
441227569Sphilip	 *
442250460Seadler	 * If there is an outstanding event-completed MCDI operation, then we
443227569Sphilip	 * will never receive the completion event (because both MCDI
444227569Sphilip	 * completions and BADASSERT events are sent to the same evq). So
445227569Sphilip	 * complete this MCDI op.
446227569Sphilip	 *
447227569Sphilip	 * This function might run in parallel with efx_mcdi_request_poll()
448227569Sphilip	 * for poll completed mcdi requests, and also with
449227569Sphilip	 * efx_mcdi_request_start() for post-watchdog completions.
450227569Sphilip	 */
451227569Sphilip	EFSYS_LOCK(enp->en_eslp, state);
452227569Sphilip	emrp = emip->emi_pending_req;
453227569Sphilip	ev_cpl = emip->emi_ev_cpl;
454227569Sphilip	if (emrp != NULL && emip->emi_ev_cpl) {
455227569Sphilip		emip->emi_pending_req = NULL;
456227569Sphilip
457227569Sphilip		emrp->emr_out_length_used = 0;
458227569Sphilip		emrp->emr_rc = rc;
459227569Sphilip		++emip->emi_aborted;
460227569Sphilip	}
461227569Sphilip
462227569Sphilip	/* Since we're running in parallel with a request, consume the
463227569Sphilip	 * status word before dropping the lock.
464227569Sphilip	 */
465227569Sphilip	if (rc == EIO || rc == EINTR) {
466227569Sphilip		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
467227569Sphilip		(void) efx_mcdi_poll_reboot(enp);
468227569Sphilip	}
469227569Sphilip
470227569Sphilip	EFSYS_UNLOCK(enp->en_eslp, state);
471227569Sphilip
472227569Sphilip	efx_mcdi_raise_exception(enp, emrp, rc);
473227569Sphilip
474227569Sphilip	if (emrp != NULL && ev_cpl)
475227569Sphilip		emtp->emt_ev_cpl(emtp->emt_context);
476227569Sphilip}
477227569Sphilip
478227569Sphilip	__checkReturn		int
479227569Sphilipefx_mcdi_version(
480227569Sphilip	__in			efx_nic_t *enp,
481227569Sphilip	__out_ecount_opt(4)	uint16_t versionp[4],
482227569Sphilip	__out_opt		uint32_t *buildp,
483227569Sphilip	__out_opt		efx_mcdi_boot_t *statusp)
484227569Sphilip{
485227569Sphilip	uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN,
486227569Sphilip		    MC_CMD_GET_BOOT_STATUS_OUT_LEN)];
487227569Sphilip	efx_mcdi_req_t req;
488227569Sphilip	efx_word_t *ver_words;
489227569Sphilip	uint16_t version[4];
490227569Sphilip	uint32_t build;
491227569Sphilip	efx_mcdi_boot_t status;
492227569Sphilip	int rc;
493227569Sphilip
494227569Sphilip	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
495227569Sphilip	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
496227569Sphilip
497227569Sphilip	EFX_STATIC_ASSERT(MC_CMD_GET_VERSION_IN_LEN == 0);
498227569Sphilip	req.emr_cmd = MC_CMD_GET_VERSION;
499227569Sphilip	req.emr_in_buf = NULL;
500227569Sphilip	req.emr_in_length = 0;
501227569Sphilip	req.emr_out_buf = outbuf;
502227569Sphilip	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
503227569Sphilip
504227569Sphilip	efx_mcdi_execute(enp, &req);
505227569Sphilip
506227569Sphilip	if (req.emr_rc != 0) {
507227569Sphilip		rc = req.emr_rc;
508227569Sphilip		goto fail1;
509227569Sphilip	}
510227569Sphilip
511227569Sphilip	/* bootrom support */
512227569Sphilip	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
513227569Sphilip		version[0] = version[1] = version[2] = version[3] = 0;
514227569Sphilip		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
515227569Sphilip
516227569Sphilip		goto version;
517227569Sphilip	}
518227569Sphilip
519227569Sphilip	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
520227569Sphilip		rc = EMSGSIZE;
521227569Sphilip		goto fail2;
522227569Sphilip	}
523227569Sphilip
524227569Sphilip	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
525227569Sphilip	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
526227569Sphilip	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
527227569Sphilip	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
528227569Sphilip	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
529227569Sphilip	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
530227569Sphilip
531227569Sphilipversion:
532227569Sphilip	/* The bootrom doesn't understand BOOT_STATUS */
533227569Sphilip	if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM) {
534227569Sphilip		status = EFX_MCDI_BOOT_ROM;
535227569Sphilip		goto out;
536227569Sphilip	}
537227569Sphilip
538227569Sphilip	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
539227569Sphilip	EFX_STATIC_ASSERT(MC_CMD_GET_BOOT_STATUS_IN_LEN == 0);
540227569Sphilip	req.emr_in_buf = NULL;
541227569Sphilip	req.emr_in_length = 0;
542227569Sphilip	req.emr_out_buf = outbuf;
543227569Sphilip	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
544227569Sphilip
545227569Sphilip	efx_mcdi_execute(enp, &req);
546227569Sphilip
547227569Sphilip	if (req.emr_rc != 0) {
548227569Sphilip		rc = req.emr_rc;
549227569Sphilip		goto fail3;
550227569Sphilip	}
551227569Sphilip
552227569Sphilip	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
553227569Sphilip		rc = EMSGSIZE;
554227569Sphilip		goto fail4;
555227569Sphilip	}
556227569Sphilip
557227569Sphilip	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
558227569Sphilip	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
559227569Sphilip		status = EFX_MCDI_BOOT_PRIMARY;
560227569Sphilip	else
561227569Sphilip		status = EFX_MCDI_BOOT_SECONDARY;
562227569Sphilip
563227569Sphilipout:
564227569Sphilip	if (versionp != NULL)
565227569Sphilip		memcpy(versionp, version, sizeof (version));
566227569Sphilip	if (buildp != NULL)
567227569Sphilip		*buildp = build;
568227569Sphilip	if (statusp != NULL)
569227569Sphilip		*statusp = status;
570227569Sphilip
571227569Sphilip	return (0);
572227569Sphilip
573227569Sphilipfail4:
574227569Sphilip	EFSYS_PROBE(fail4);
575227569Sphilipfail3:
576227569Sphilip	EFSYS_PROBE(fail3);
577227569Sphilipfail2:
578227569Sphilip	EFSYS_PROBE(fail2);
579227569Sphilipfail1:
580227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
581227569Sphilip
582227569Sphilip	return (rc);
583227569Sphilip}
584227569Sphilip
585227569Sphilip	__checkReturn	int
586227569Sphilipefx_mcdi_init(
587227569Sphilip	__in		efx_nic_t *enp,
588227569Sphilip	__in		const efx_mcdi_transport_t *mtp)
589227569Sphilip{
590227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
591227569Sphilip	efx_oword_t oword;
592227569Sphilip	unsigned int portnum;
593227569Sphilip	int rc;
594227569Sphilip
595227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
596227569Sphilip	enp->en_mod_flags |= EFX_MOD_MCDI;
597227569Sphilip
598227569Sphilip	if (enp->en_family == EFX_FAMILY_FALCON)
599227569Sphilip		return (0);
600227569Sphilip
601227569Sphilip	emip->emi_mtp = mtp;
602227569Sphilip
603227569Sphilip	/* Determine the port number to use for MCDI */
604227569Sphilip	EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
605227569Sphilip	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
606227569Sphilip
607227569Sphilip	if (portnum == 0) {
608227569Sphilip		/* Presumably booted from ROM; only MCDI port 1 will work */
609227569Sphilip		emip->emi_port = 1;
610227569Sphilip	} else if (portnum <= 2) {
611227569Sphilip		emip->emi_port = portnum;
612227569Sphilip	} else {
613227569Sphilip		rc = EINVAL;
614227569Sphilip		goto fail1;
615227569Sphilip	}
616227569Sphilip
617227569Sphilip	/*
618227569Sphilip	 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
619227569Sphilip	 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
620227569Sphilip	 * assertion handler.
621227569Sphilip	 */
622227569Sphilip	(void) efx_mcdi_poll_reboot(enp);
623227569Sphilip
624227569Sphilip	return (0);
625227569Sphilip
626227569Sphilipfail1:
627227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
628227569Sphilip
629227569Sphilip	enp->en_mod_flags &= ~EFX_MOD_MCDI;
630227569Sphilip
631227569Sphilip	return (rc);
632227569Sphilip}
633227569Sphilip
634227569Sphilip
635227569Sphilip	__checkReturn	int
636227569Sphilipefx_mcdi_reboot(
637227569Sphilip	__in		efx_nic_t *enp)
638227569Sphilip{
639227569Sphilip	uint8_t payload[MC_CMD_REBOOT_IN_LEN];
640227569Sphilip	efx_mcdi_req_t req;
641227569Sphilip	int rc;
642227569Sphilip
643227569Sphilip	/*
644227569Sphilip	 * We could require the caller to have caused en_mod_flags=0 to
645227569Sphilip	 * call this function. This doesn't help the other port though,
646227569Sphilip	 * who's about to get the MC ripped out from underneath them.
647227569Sphilip	 * Since they have to cope with the subsequent fallout of MCDI
648227569Sphilip	 * failures, we should as well.
649227569Sphilip	 */
650227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
651227569Sphilip
652227569Sphilip	req.emr_cmd = MC_CMD_REBOOT;
653227569Sphilip	req.emr_in_buf = payload;
654227569Sphilip	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
655227569Sphilip	req.emr_out_buf = NULL;
656227569Sphilip	req.emr_out_length = 0;
657227569Sphilip
658227569Sphilip	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0);
659227569Sphilip
660227569Sphilip	efx_mcdi_execute(enp, &req);
661227569Sphilip
662227569Sphilip	/* Invert EIO */
663227569Sphilip	if (req.emr_rc != EIO) {
664227569Sphilip		rc = EIO;
665227569Sphilip		goto fail1;
666227569Sphilip	}
667227569Sphilip
668227569Sphilip	return (0);
669227569Sphilip
670227569Sphilipfail1:
671227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
672227569Sphilip
673227569Sphilip	return (rc);
674227569Sphilip}
675227569Sphilip
676227569Sphilip	__checkReturn	boolean_t
677227569Sphilipefx_mcdi_request_abort(
678227569Sphilip	__in		efx_nic_t *enp)
679227569Sphilip{
680227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
681227569Sphilip	efx_mcdi_req_t *emrp;
682227569Sphilip	boolean_t aborted;
683227569Sphilip	int state;
684227569Sphilip
685227569Sphilip	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
686227569Sphilip	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
687227569Sphilip
688227569Sphilip	/*
689227569Sphilip	 * efx_mcdi_ev_* may have already completed this event, and be
690227569Sphilip	 * spinning/blocked on the upper layer lock. So it *is* legitimate
691227569Sphilip	 * to for emi_pending_req to be NULL. If there is a pending event
692227569Sphilip	 * completed request, then provide a "credit" to allow
693227569Sphilip	 * efx_mcdi_ev_cpl() to accept a single spurious completion.
694227569Sphilip	 */
695227569Sphilip	EFSYS_LOCK(enp->en_eslp, state);
696227569Sphilip	emrp = emip->emi_pending_req;
697227569Sphilip	aborted = (emrp != NULL);
698227569Sphilip	if (aborted) {
699227569Sphilip		emip->emi_pending_req = NULL;
700227569Sphilip
701227569Sphilip		/* Error the request */
702227569Sphilip		emrp->emr_out_length_used = 0;
703227569Sphilip		emrp->emr_rc = ETIMEDOUT;
704227569Sphilip
705227569Sphilip		/* Provide a credit for seqno/emr_pending_req mismatches */
706227569Sphilip		if (emip->emi_ev_cpl)
707227569Sphilip			++emip->emi_aborted;
708227569Sphilip
709227569Sphilip		/*
710227569Sphilip		 * The upper layer has called us, so we don't
711227569Sphilip		 * need to complete the request.
712227569Sphilip		 */
713227569Sphilip	}
714227569Sphilip	EFSYS_UNLOCK(enp->en_eslp, state);
715227569Sphilip
716227569Sphilip	return (aborted);
717227569Sphilip}
718227569Sphilip
719227569Sphilip			void
720227569Sphilipefx_mcdi_fini(
721227569Sphilip	__in		efx_nic_t *enp)
722227569Sphilip{
723227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
724227569Sphilip
725227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
726227569Sphilip	enp->en_mod_flags &= ~EFX_MOD_MCDI;
727227569Sphilip
728227569Sphilip	if (~(enp->en_features) & EFX_FEATURE_MCDI)
729227569Sphilip		return;
730227569Sphilip
731227569Sphilip	emip->emi_mtp = NULL;
732227569Sphilip	emip->emi_port = 0;
733227569Sphilip	emip->emi_aborted = 0;
734227569Sphilip}
735227569Sphilip
736227569Sphilip#endif	/* EFSYS_OPT_MCDI */
737