xref: /freebsd/sys/dev/sfxge/common/siena_mcdi.c (revision a476e3a5678de5ca7ad765315f28308410d4f09e)
1 /*-
2  * Copyright (c) 2012-2015 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include "efsys.h"
35 #include "efx.h"
36 #include "efx_impl.h"
37 
38 #if EFSYS_OPT_SIENA && EFSYS_OPT_MCDI
39 
40 #define	SIENA_MCDI_PDU(_emip)			\
41 	(((emip)->emi_port == 1)		\
42 	? MC_SMEM_P0_PDU_OFST >> 2		\
43 	: MC_SMEM_P1_PDU_OFST >> 2)
44 
45 #define	SIENA_MCDI_DOORBELL(_emip)		\
46 	(((emip)->emi_port == 1)		\
47 	? MC_SMEM_P0_DOORBELL_OFST >> 2		\
48 	: MC_SMEM_P1_DOORBELL_OFST >> 2)
49 
50 #define	SIENA_MCDI_STATUS(_emip)		\
51 	(((emip)->emi_port == 1)		\
52 	? MC_SMEM_P0_STATUS_OFST >> 2		\
53 	: MC_SMEM_P1_STATUS_OFST >> 2)
54 
55 
56 			void
57 siena_mcdi_request_copyin(
58 	__in		efx_nic_t *enp,
59 	__in		efx_mcdi_req_t *emrp,
60 	__in		unsigned int seq,
61 	__in		boolean_t ev_cpl,
62 	__in		boolean_t new_epoch)
63 {
64 #if EFSYS_OPT_MCDI_LOGGING
65 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
66 #endif
67 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
68 	efx_dword_t hdr;
69 	efx_dword_t dword;
70 	unsigned int xflags;
71 	unsigned int pdur;
72 	unsigned int dbr;
73 	unsigned int pos;
74 
75 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
76 	_NOTE(ARGUNUSED(new_epoch))
77 
78 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
79 	pdur = SIENA_MCDI_PDU(emip);
80 	dbr = SIENA_MCDI_DOORBELL(emip);
81 
82 	xflags = 0;
83 	if (ev_cpl)
84 		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
85 
86 	/* Construct the header in shared memory */
87 	EFX_POPULATE_DWORD_6(hdr,
88 			    MCDI_HEADER_CODE, emrp->emr_cmd,
89 			    MCDI_HEADER_RESYNC, 1,
90 			    MCDI_HEADER_DATALEN, emrp->emr_in_length,
91 			    MCDI_HEADER_SEQ, seq,
92 			    MCDI_HEADER_RESPONSE, 0,
93 			    MCDI_HEADER_XFLAGS, xflags);
94 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_TRUE);
95 
96 #if EFSYS_OPT_MCDI_LOGGING
97 	if (emtp->emt_logger != NULL) {
98 		emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST,
99 		    &hdr, sizeof (hdr),
100 		    emrp->emr_in_buf, emrp->emr_in_length);
101 	}
102 #endif /* EFSYS_OPT_MCDI_LOGGING */
103 
104 	/* Construct the payload */
105 	for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
106 		memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
107 		    MIN(sizeof (dword), emrp->emr_in_length - pos));
108 		EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
109 		    pdur + 1 + (pos >> 2), &dword, B_FALSE);
110 	}
111 
112 	/* Ring the doorbell */
113 	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
114 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
115 }
116 
117 			void
118 siena_mcdi_request_copyout(
119 	__in		efx_nic_t *enp,
120 	__in		efx_mcdi_req_t *emrp)
121 {
122 #if EFSYS_OPT_MCDI_LOGGING
123 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
124 	efx_dword_t hdr;
125 #endif
126 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
127 	unsigned int pos;
128 	unsigned int pdur;
129 	efx_dword_t data;
130 
131 	pdur = SIENA_MCDI_PDU(emip);
132 
133 	/* Copy payload out if caller supplied buffer */
134 	if (emrp->emr_out_buf != NULL) {
135 		size_t bytes = MIN(emrp->emr_out_length_used,
136 				    emrp->emr_out_length);
137 		for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) {
138 			EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
139 			    pdur + 1 + (pos >> 2), &data, B_FALSE);
140 			memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data,
141 			    MIN(sizeof (data), bytes - pos));
142 		}
143 	}
144 
145 #if EFSYS_OPT_MCDI_LOGGING
146 	if (emtp->emt_logger != NULL) {
147 		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
148 
149 		emtp->emt_logger(emtp->emt_context,
150 		    EFX_LOG_MCDI_RESPONSE,
151 		    &hdr, sizeof (hdr),
152 		    emrp->emr_out_buf, emrp->emr_out_length_used);
153 	}
154 #endif /* EFSYS_OPT_MCDI_LOGGING */
155 }
156 
157 			efx_rc_t
158 siena_mcdi_poll_reboot(
159 	__in		efx_nic_t *enp)
160 {
161 #ifndef EFX_GRACEFUL_MC_REBOOT
162  	/*
163 	 * This function is not being used properly.
164 	 * Until its callers are fixed, it should always return 0.
165 	 */
166 	_NOTE(ARGUNUSED(enp))
167 	return (0);
168 #else
169 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
170 	unsigned int rebootr;
171 	efx_dword_t dword;
172 	uint32_t value;
173 
174 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
175 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
176 	rebootr = SIENA_MCDI_STATUS(emip);
177 
178 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
179 	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
180 
181 	if (value == 0)
182 		return (0);
183 
184 	EFX_ZERO_DWORD(dword);
185 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
186 
187 	if (value == MC_STATUS_DWORD_ASSERT)
188 		return (EINTR);
189 	else
190 		return (EIO);
191 #endif
192 }
193 
194 static	__checkReturn	boolean_t
195 siena_mcdi_poll_response(
196 	__in		efx_nic_t *enp)
197 {
198 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
199 	efx_dword_t hdr;
200 	unsigned int pdur;
201 
202 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
203 	pdur = SIENA_MCDI_PDU(emip);
204 
205 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
206 	return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
207 }
208 
209 	__checkReturn	boolean_t
210 siena_mcdi_request_poll(
211 	__in		efx_nic_t *enp)
212 {
213 #if EFSYS_OPT_MCDI_LOGGING
214 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
215 #endif
216 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
217 	efx_mcdi_req_t *emrp;
218 	efx_dword_t hdr;
219 	unsigned int pdur;
220 	unsigned int seq;
221 	unsigned int length;
222 	int state;
223 	efx_rc_t rc;
224 
225 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
226 
227 	/* Serialise against post-watchdog efx_mcdi_ev* */
228 	EFSYS_LOCK(enp->en_eslp, state);
229 
230 	EFSYS_ASSERT(emip->emi_pending_req != NULL);
231 	EFSYS_ASSERT(!emip->emi_ev_cpl);
232 	emrp = emip->emi_pending_req;
233 
234 	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
235 	if (emip->emi_poll_cnt++ == 0) {
236 		if ((rc = siena_mcdi_poll_reboot(enp)) != 0) {
237 			emip->emi_pending_req = NULL;
238 			EFSYS_UNLOCK(enp->en_eslp, state);
239 
240 			goto fail1;
241 		}
242 	}
243 
244 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
245 	pdur = SIENA_MCDI_PDU(emip);
246 
247 	/* Check if a response is available */
248 	if (siena_mcdi_poll_response(enp) == B_FALSE) {
249 		EFSYS_UNLOCK(enp->en_eslp, state);
250 		return (B_FALSE);
251 	}
252 
253 	/* Read the response header */
254 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
255 
256 	/* Request complete */
257 	emip->emi_pending_req = NULL;
258 	seq = (emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ);
259 
260 	/* Check for synchronous reboot */
261 	if (EFX_DWORD_FIELD(hdr, MCDI_HEADER_ERROR) != 0 &&
262 	    EFX_DWORD_FIELD(hdr, MCDI_HEADER_DATALEN) == 0) {
263 		/* Consume status word */
264 		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
265 		siena_mcdi_poll_reboot(enp);
266 		EFSYS_UNLOCK(enp->en_eslp, state);
267 		rc = EIO;
268 		goto fail2;
269 	}
270 
271 	EFSYS_UNLOCK(enp->en_eslp, state);
272 
273 	/* Check that the returned data is consistent */
274 	if (EFX_DWORD_FIELD(hdr, MCDI_HEADER_CODE) != emrp->emr_cmd ||
275 	    EFX_DWORD_FIELD(hdr, MCDI_HEADER_SEQ) != seq) {
276 		/* Response is for a different request */
277 		rc = EIO;
278 		goto fail3;
279 	}
280 
281 	length = EFX_DWORD_FIELD(hdr, MCDI_HEADER_DATALEN);
282 	if (EFX_DWORD_FIELD(hdr, MCDI_HEADER_ERROR)) {
283 		efx_dword_t errdword;
284 		int errcode;
285 
286 		EFSYS_ASSERT3U(length, ==, 4);
287 		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
288 		    pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2),
289 		    &errdword, B_FALSE);
290 		errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
291 
292 #if EFSYS_OPT_MCDI_LOGGING
293 		if (emtp->emt_logger != NULL) {
294 			emtp->emt_logger(emtp->emt_context,
295 			    EFX_LOG_MCDI_RESPONSE,
296 			    &hdr, sizeof (hdr),
297 			    &errdword, sizeof (errdword));
298 		}
299 #endif /* EFSYS_OPT_MCDI_LOGGING */
300 
301 		rc = efx_mcdi_request_errcode(errcode);
302 		if (!emrp->emr_quiet) {
303 			EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
304 			    int, errcode);
305 		}
306 		goto fail4;
307 
308 	} else {
309 		emrp->emr_out_length_used = length;
310 		emrp->emr_rc = 0;
311 		siena_mcdi_request_copyout(enp, emrp);
312 	}
313 
314 	goto out;
315 
316 fail4:
317 	if (!emrp->emr_quiet)
318 		EFSYS_PROBE(fail4);
319 fail3:
320 	if (!emrp->emr_quiet)
321 		EFSYS_PROBE(fail3);
322 fail2:
323 	if (!emrp->emr_quiet)
324 		EFSYS_PROBE(fail2);
325 fail1:
326 	if (!emrp->emr_quiet)
327 		EFSYS_PROBE1(fail1, efx_rc_t, rc);
328 
329 	/* Fill out error state */
330 	emrp->emr_rc = rc;
331 	emrp->emr_out_length_used = 0;
332 
333 	/* Reboot/Assertion */
334 	if (rc == EIO || rc == EINTR)
335 		efx_mcdi_raise_exception(enp, emrp, rc);
336 
337 out:
338 	return (B_TRUE);
339 }
340 
341 	__checkReturn	efx_rc_t
342 siena_mcdi_init(
343 	__in		efx_nic_t *enp,
344 	__in		const efx_mcdi_transport_t *mtp)
345 {
346 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
347 	efx_oword_t oword;
348 	unsigned int portnum;
349 	efx_rc_t rc;
350 
351 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
352 
353 	/* Determine the port number to use for MCDI */
354 	EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
355 	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
356 
357 	if (portnum == 0) {
358 		/* Presumably booted from ROM; only MCDI port 1 will work */
359 		emip->emi_port = 1;
360 	} else if (portnum <= 2) {
361 		emip->emi_port = portnum;
362 	} else {
363 		rc = EINVAL;
364 		goto fail1;
365 	}
366 
367 	/*
368 	 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
369 	 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
370 	 * assertion handler.
371 	 */
372 	(void) siena_mcdi_poll_reboot(enp);
373 
374 	return (0);
375 
376 fail1:
377 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
378 
379 	return (rc);
380 }
381 
382 			void
383 siena_mcdi_fini(
384 	__in		efx_nic_t *enp)
385 {
386 }
387 
388 	__checkReturn	efx_rc_t
389 siena_mcdi_fw_update_supported(
390 	__in		efx_nic_t *enp,
391 	__out		boolean_t *supportedp)
392 {
393 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
394 
395 	*supportedp = B_TRUE;
396 
397 	return (0);
398 }
399 
400 	__checkReturn	efx_rc_t
401 siena_mcdi_macaddr_change_supported(
402 	__in		efx_nic_t *enp,
403 	__out		boolean_t *supportedp)
404 {
405 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
406 
407 	*supportedp = B_TRUE;
408 
409 	return (0);
410 }
411 
412 	__checkReturn	efx_rc_t
413 siena_mcdi_link_control_supported(
414 	__in		efx_nic_t *enp,
415 	__out		boolean_t *supportedp)
416 {
417 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
418 
419 	*supportedp = B_TRUE;
420 
421 	return (0);
422 }
423 
424 #endif	/* EFSYS_OPT_SIENA && EFSYS_OPT_MCDI */
425