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