1*49ef7e06SGarrett D'Amore /*
2*49ef7e06SGarrett D'Amore  * Copyright (c) 2009-2016 Solarflare Communications Inc.
3*49ef7e06SGarrett D'Amore  * All rights reserved.
4*49ef7e06SGarrett D'Amore  *
5*49ef7e06SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
6*49ef7e06SGarrett D'Amore  * modification, are permitted provided that the following conditions are met:
7*49ef7e06SGarrett D'Amore  *
8*49ef7e06SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright notice,
9*49ef7e06SGarrett D'Amore  *    this list of conditions and the following disclaimer.
10*49ef7e06SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright notice,
11*49ef7e06SGarrett D'Amore  *    this list of conditions and the following disclaimer in the documentation
12*49ef7e06SGarrett D'Amore  *    and/or other materials provided with the distribution.
13*49ef7e06SGarrett D'Amore  *
14*49ef7e06SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15*49ef7e06SGarrett D'Amore  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16*49ef7e06SGarrett D'Amore  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17*49ef7e06SGarrett D'Amore  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18*49ef7e06SGarrett D'Amore  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19*49ef7e06SGarrett D'Amore  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20*49ef7e06SGarrett D'Amore  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21*49ef7e06SGarrett D'Amore  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22*49ef7e06SGarrett D'Amore  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23*49ef7e06SGarrett D'Amore  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24*49ef7e06SGarrett D'Amore  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*49ef7e06SGarrett D'Amore  *
26*49ef7e06SGarrett D'Amore  * The views and conclusions contained in the software and documentation are
27*49ef7e06SGarrett D'Amore  * those of the authors and should not be interpreted as representing official
28*49ef7e06SGarrett D'Amore  * policies, either expressed or implied, of the FreeBSD Project.
29*49ef7e06SGarrett D'Amore  */
30*49ef7e06SGarrett D'Amore 
31*49ef7e06SGarrett D'Amore #include <sys/types.h>
32*49ef7e06SGarrett D'Amore #include <sys/ddi.h>
33*49ef7e06SGarrett D'Amore #include <sys/sunddi.h>
34*49ef7e06SGarrett D'Amore #include <sys/stream.h>
35*49ef7e06SGarrett D'Amore #include <sys/dlpi.h>
36*49ef7e06SGarrett D'Amore #include <sys/pci.h>
37*49ef7e06SGarrett D'Amore 
38*49ef7e06SGarrett D'Amore #include "sfxge.h"
39*49ef7e06SGarrett D'Amore #include "efsys.h"
40*49ef7e06SGarrett D'Amore #include "efx.h"
41*49ef7e06SGarrett D'Amore #include "efx_mcdi.h"
42*49ef7e06SGarrett D'Amore #include "efx_regs_mcdi.h"
43*49ef7e06SGarrett D'Amore 
44*49ef7e06SGarrett D'Amore /* MAC DMA attributes */
45*49ef7e06SGarrett D'Amore static ddi_device_acc_attr_t sfxge_mcdi_devacc = {
46*49ef7e06SGarrett D'Amore 
47*49ef7e06SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
48*49ef7e06SGarrett D'Amore 	DDI_NEVERSWAP_ACC,	/* devacc_attr_endian_flags */
49*49ef7e06SGarrett D'Amore 	DDI_STRICTORDER_ACC	/* devacc_attr_dataorder */
50*49ef7e06SGarrett D'Amore };
51*49ef7e06SGarrett D'Amore 
52*49ef7e06SGarrett D'Amore static ddi_dma_attr_t sfxge_mcdi_dma_attr = {
53*49ef7e06SGarrett D'Amore 	DMA_ATTR_V0,		/* dma_attr_version	*/
54*49ef7e06SGarrett D'Amore 	0,			/* dma_attr_addr_lo	*/
55*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_addr_hi	*/
56*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_count_max	*/
57*49ef7e06SGarrett D'Amore 	0x1000,			/* dma_attr_align	*/
58*49ef7e06SGarrett D'Amore 	0xffffffff,		/* dma_attr_burstsizes	*/
59*49ef7e06SGarrett D'Amore 	1,			/* dma_attr_minxfer	*/
60*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_maxxfer	*/
61*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_seg		*/
62*49ef7e06SGarrett D'Amore 	1,			/* dma_attr_sgllen	*/
63*49ef7e06SGarrett D'Amore 	1,			/* dma_attr_granular	*/
64*49ef7e06SGarrett D'Amore 	0			/* dma_attr_flags	*/
65*49ef7e06SGarrett D'Amore };
66*49ef7e06SGarrett D'Amore 
67*49ef7e06SGarrett D'Amore /*
68*49ef7e06SGarrett D'Amore  * Notes on MCDI operation:
69*49ef7e06SGarrett D'Amore  * ------------------------
70*49ef7e06SGarrett D'Amore  * MCDI requests can be made in arbitrary thread context, and as a synchronous
71*49ef7e06SGarrett D'Amore  * API must therefore block until the response is available from the MC, or
72*49ef7e06SGarrett D'Amore  * a watchdog timeout occurs.
73*49ef7e06SGarrett D'Amore  *
74*49ef7e06SGarrett D'Amore  * This interacts badly with the limited number of worker threads (2 per CPU)
75*49ef7e06SGarrett D'Amore  * used by the Solaris callout subsystem to invoke timeout handlers. If both
76*49ef7e06SGarrett D'Amore  * worker threads are blocked (e.g. waiting for a condvar or mutex) then timeout
77*49ef7e06SGarrett D'Amore  * processing is deadlocked on that CPU, causing system failure.
78*49ef7e06SGarrett D'Amore  *
79*49ef7e06SGarrett D'Amore  * For this reason the driver does not use event based MCDI completion, as this
80*49ef7e06SGarrett D'Amore  * leads to numerous paths involving timeouts and reentrant GLDv3 entrypoints
81*49ef7e06SGarrett D'Amore  * that result in a deadlocked system.
82*49ef7e06SGarrett D'Amore  */
83*49ef7e06SGarrett D'Amore #define	SFXGE_MCDI_POLL_INTERVAL	10		/* 10us in 1us units */
84*49ef7e06SGarrett D'Amore #define	SFXGE_MCDI_WATCHDOG_INTERVAL	10000000	/* 10s in 1us units */
85*49ef7e06SGarrett D'Amore 
86*49ef7e06SGarrett D'Amore 
87*49ef7e06SGarrett D'Amore /* Acquire exclusive access to MCDI for the duration of a request */
88*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_acquire(sfxge_mcdi_t * smp)89*49ef7e06SGarrett D'Amore sfxge_mcdi_acquire(sfxge_mcdi_t *smp)
90*49ef7e06SGarrett D'Amore {
91*49ef7e06SGarrett D'Amore 	mutex_enter(&(smp->sm_lock));
92*49ef7e06SGarrett D'Amore 	ASSERT3U(smp->sm_state, !=, SFXGE_MCDI_UNINITIALIZED);
93*49ef7e06SGarrett D'Amore 
94*49ef7e06SGarrett D'Amore 	while (smp->sm_state != SFXGE_MCDI_INITIALIZED) {
95*49ef7e06SGarrett D'Amore 		(void) cv_wait(&(smp->sm_kv), &(smp->sm_lock));
96*49ef7e06SGarrett D'Amore 	}
97*49ef7e06SGarrett D'Amore 	smp->sm_state = SFXGE_MCDI_BUSY;
98*49ef7e06SGarrett D'Amore 
99*49ef7e06SGarrett D'Amore 	mutex_exit(&(smp->sm_lock));
100*49ef7e06SGarrett D'Amore }
101*49ef7e06SGarrett D'Amore 
102*49ef7e06SGarrett D'Amore 
103*49ef7e06SGarrett D'Amore /* Release ownership of MCDI on request completion */
104*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_release(sfxge_mcdi_t * smp)105*49ef7e06SGarrett D'Amore sfxge_mcdi_release(sfxge_mcdi_t *smp)
106*49ef7e06SGarrett D'Amore {
107*49ef7e06SGarrett D'Amore 	mutex_enter(&(smp->sm_lock));
108*49ef7e06SGarrett D'Amore 	ASSERT((smp->sm_state == SFXGE_MCDI_BUSY) ||
109*49ef7e06SGarrett D'Amore 	    (smp->sm_state == SFXGE_MCDI_COMPLETED));
110*49ef7e06SGarrett D'Amore 
111*49ef7e06SGarrett D'Amore 	smp->sm_state = SFXGE_MCDI_INITIALIZED;
112*49ef7e06SGarrett D'Amore 	cv_broadcast(&(smp->sm_kv));
113*49ef7e06SGarrett D'Amore 
114*49ef7e06SGarrett D'Amore 	mutex_exit(&(smp->sm_lock));
115*49ef7e06SGarrett D'Amore }
116*49ef7e06SGarrett D'Amore 
117*49ef7e06SGarrett D'Amore 
118*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_timeout(sfxge_t * sp)119*49ef7e06SGarrett D'Amore sfxge_mcdi_timeout(sfxge_t *sp)
120*49ef7e06SGarrett D'Amore {
121*49ef7e06SGarrett D'Amore 	dev_info_t *dip = sp->s_dip;
122*49ef7e06SGarrett D'Amore 
123*49ef7e06SGarrett D'Amore 	dev_err(dip, CE_WARN, SFXGE_CMN_ERR "MC_TIMEOUT");
124*49ef7e06SGarrett D'Amore 
125*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(mcdi_timeout);
126*49ef7e06SGarrett D'Amore 	(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR,
127*49ef7e06SGarrett D'Amore 	    "MCDI timeout", 0);
128*49ef7e06SGarrett D'Amore }
129*49ef7e06SGarrett D'Amore 
130*49ef7e06SGarrett D'Amore 
131*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_poll(sfxge_t * sp)132*49ef7e06SGarrett D'Amore sfxge_mcdi_poll(sfxge_t *sp)
133*49ef7e06SGarrett D'Amore {
134*49ef7e06SGarrett D'Amore 	efx_nic_t *enp = sp->s_enp;
135*49ef7e06SGarrett D'Amore 	clock_t timeout;
136*49ef7e06SGarrett D'Amore 	boolean_t aborted;
137*49ef7e06SGarrett D'Amore 
138*49ef7e06SGarrett D'Amore 	/* Poll until request completes or timeout */
139*49ef7e06SGarrett D'Amore 	timeout = ddi_get_lbolt() + drv_usectohz(SFXGE_MCDI_WATCHDOG_INTERVAL);
140*49ef7e06SGarrett D'Amore 	while (efx_mcdi_request_poll(enp) == B_FALSE) {
141*49ef7e06SGarrett D'Amore 
142*49ef7e06SGarrett D'Amore 		/* No response received yet */
143*49ef7e06SGarrett D'Amore 		if (ddi_get_lbolt() > timeout) {
144*49ef7e06SGarrett D'Amore 			/* Timeout expired */
145*49ef7e06SGarrett D'Amore 			goto fail;
146*49ef7e06SGarrett D'Amore 		}
147*49ef7e06SGarrett D'Amore 
148*49ef7e06SGarrett D'Amore 		/* Short delay to avoid excessive PCIe traffic */
149*49ef7e06SGarrett D'Amore 		drv_usecwait(SFXGE_MCDI_POLL_INTERVAL);
150*49ef7e06SGarrett D'Amore 	}
151*49ef7e06SGarrett D'Amore 
152*49ef7e06SGarrett D'Amore 	/* Request completed (or polling failed) */
153*49ef7e06SGarrett D'Amore 	return;
154*49ef7e06SGarrett D'Amore 
155*49ef7e06SGarrett D'Amore fail:
156*49ef7e06SGarrett D'Amore 	/* Timeout before request completion */
157*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail);
158*49ef7e06SGarrett D'Amore 	aborted = efx_mcdi_request_abort(enp);
159*49ef7e06SGarrett D'Amore 	ASSERT(aborted);
160*49ef7e06SGarrett D'Amore 	sfxge_mcdi_timeout(sp);
161*49ef7e06SGarrett D'Amore }
162*49ef7e06SGarrett D'Amore 
163*49ef7e06SGarrett D'Amore 
164*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_execute(void * arg,efx_mcdi_req_t * emrp)165*49ef7e06SGarrett D'Amore sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
166*49ef7e06SGarrett D'Amore {
167*49ef7e06SGarrett D'Amore 	sfxge_t *sp = (sfxge_t *)arg;
168*49ef7e06SGarrett D'Amore 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
169*49ef7e06SGarrett D'Amore 
170*49ef7e06SGarrett D'Amore 	sfxge_mcdi_acquire(smp);
171*49ef7e06SGarrett D'Amore 
172*49ef7e06SGarrett D'Amore 	/* Issue request and poll for completion */
173*49ef7e06SGarrett D'Amore 	efx_mcdi_request_start(sp->s_enp, emrp, B_FALSE);
174*49ef7e06SGarrett D'Amore 	sfxge_mcdi_poll(sp);
175*49ef7e06SGarrett D'Amore 
176*49ef7e06SGarrett D'Amore 	sfxge_mcdi_release(smp);
177*49ef7e06SGarrett D'Amore }
178*49ef7e06SGarrett D'Amore 
179*49ef7e06SGarrett D'Amore 
180*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_ev_cpl(void * arg)181*49ef7e06SGarrett D'Amore sfxge_mcdi_ev_cpl(void *arg)
182*49ef7e06SGarrett D'Amore {
183*49ef7e06SGarrett D'Amore 	sfxge_t *sp = (sfxge_t *)arg;
184*49ef7e06SGarrett D'Amore 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
185*49ef7e06SGarrett D'Amore 
186*49ef7e06SGarrett D'Amore 	mutex_enter(&(smp->sm_lock));
187*49ef7e06SGarrett D'Amore 	ASSERT(smp->sm_state == SFXGE_MCDI_BUSY);
188*49ef7e06SGarrett D'Amore 	smp->sm_state = SFXGE_MCDI_COMPLETED;
189*49ef7e06SGarrett D'Amore 	cv_broadcast(&(smp->sm_kv));
190*49ef7e06SGarrett D'Amore 	mutex_exit(&(smp->sm_lock));
191*49ef7e06SGarrett D'Amore }
192*49ef7e06SGarrett D'Amore 
193*49ef7e06SGarrett D'Amore 
194*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_exception(void * arg,efx_mcdi_exception_t eme)195*49ef7e06SGarrett D'Amore sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
196*49ef7e06SGarrett D'Amore {
197*49ef7e06SGarrett D'Amore 	sfxge_t *sp = (sfxge_t *)arg;
198*49ef7e06SGarrett D'Amore 	const char *reason;
199*49ef7e06SGarrett D'Amore 
200*49ef7e06SGarrett D'Amore 	if (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
201*49ef7e06SGarrett D'Amore 		reason = "MC_REBOOT";
202*49ef7e06SGarrett D'Amore 	else if (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
203*49ef7e06SGarrett D'Amore 		reason = "MC_BADASSERT";
204*49ef7e06SGarrett D'Amore 	else
205*49ef7e06SGarrett D'Amore 		reason = "MC_UNKNOWN";
206*49ef7e06SGarrett D'Amore 
207*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(mcdi_exception);
208*49ef7e06SGarrett D'Amore 	/* sfxge_evq_t->se_lock held */
209*49ef7e06SGarrett D'Amore 	(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR, reason, 0);
210*49ef7e06SGarrett D'Amore }
211*49ef7e06SGarrett D'Amore 
212*49ef7e06SGarrett D'Amore #if EFSYS_OPT_MCDI_LOGGING
213*49ef7e06SGarrett D'Amore #define	SFXGE_MCDI_LOG_BUF_SIZE	128
214*49ef7e06SGarrett D'Amore 
215*49ef7e06SGarrett D'Amore static size_t
sfxge_mcdi_do_log(char * buffer,void * data,size_t data_size,size_t pfxsize,size_t position)216*49ef7e06SGarrett D'Amore sfxge_mcdi_do_log(char *buffer, void *data, size_t data_size,
217*49ef7e06SGarrett D'Amore     size_t pfxsize, size_t position)
218*49ef7e06SGarrett D'Amore {
219*49ef7e06SGarrett D'Amore 	uint32_t *words = data;
220*49ef7e06SGarrett D'Amore 	size_t i;
221*49ef7e06SGarrett D'Amore 
222*49ef7e06SGarrett D'Amore 	for (i = 0; i < data_size; i += sizeof (*words)) {
223*49ef7e06SGarrett D'Amore 		if (position + 2 * sizeof (*words) + 1 >=
224*49ef7e06SGarrett D'Amore 		    SFXGE_MCDI_LOG_BUF_SIZE) {
225*49ef7e06SGarrett D'Amore 			buffer[position] = '\0';
226*49ef7e06SGarrett D'Amore 			cmn_err(CE_NOTE, "%s \\", buffer);
227*49ef7e06SGarrett D'Amore 			position = pfxsize;
228*49ef7e06SGarrett D'Amore 		}
229*49ef7e06SGarrett D'Amore 		snprintf(buffer + position, SFXGE_MCDI_LOG_BUF_SIZE - position,
230*49ef7e06SGarrett D'Amore 		    " %08x", *words);
231*49ef7e06SGarrett D'Amore 		words++;
232*49ef7e06SGarrett D'Amore 		position += 2 * sizeof (uint32_t) + 1;
233*49ef7e06SGarrett D'Amore 	}
234*49ef7e06SGarrett D'Amore 	return (position);
235*49ef7e06SGarrett D'Amore }
236*49ef7e06SGarrett D'Amore 
237*49ef7e06SGarrett D'Amore 
238*49ef7e06SGarrett D'Amore static void
sfxge_mcdi_logger(void * arg,efx_log_msg_t type,void * header,size_t header_size,void * data,size_t data_size)239*49ef7e06SGarrett D'Amore sfxge_mcdi_logger(void *arg, efx_log_msg_t type,
240*49ef7e06SGarrett D'Amore     void *header, size_t header_size, void *data, size_t data_size)
241*49ef7e06SGarrett D'Amore {
242*49ef7e06SGarrett D'Amore 	sfxge_t *sp = (sfxge_t *)arg;
243*49ef7e06SGarrett D'Amore 	char buffer[SFXGE_MCDI_LOG_BUF_SIZE];
244*49ef7e06SGarrett D'Amore 	size_t pfxsize;
245*49ef7e06SGarrett D'Amore 	size_t start;
246*49ef7e06SGarrett D'Amore 
247*49ef7e06SGarrett D'Amore 	if (!sp->s_mcdi_logging)
248*49ef7e06SGarrett D'Amore 		return;
249*49ef7e06SGarrett D'Amore 
250*49ef7e06SGarrett D'Amore 	pfxsize = snprintf(buffer, sizeof (buffer),
251*49ef7e06SGarrett D'Amore 	    "sfc %04x:%02x:%02x.%02x %s%d MCDI RPC %s:",
252*49ef7e06SGarrett D'Amore 	    0,
253*49ef7e06SGarrett D'Amore 	    PCI_REG_BUS_G(sp->s_bus_addr),
254*49ef7e06SGarrett D'Amore 	    PCI_REG_DEV_G(sp->s_bus_addr),
255*49ef7e06SGarrett D'Amore 	    PCI_REG_FUNC_G(sp->s_bus_addr),
256*49ef7e06SGarrett D'Amore 	    ddi_driver_name(sp->s_dip),
257*49ef7e06SGarrett D'Amore 	    ddi_get_instance(sp->s_dip),
258*49ef7e06SGarrett D'Amore 	    type == EFX_LOG_MCDI_REQUEST ? "REQ" :
259*49ef7e06SGarrett D'Amore 	    type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???");
260*49ef7e06SGarrett D'Amore 	start = sfxge_mcdi_do_log(buffer, header, header_size,
261*49ef7e06SGarrett D'Amore 	    pfxsize, pfxsize);
262*49ef7e06SGarrett D'Amore 	start = sfxge_mcdi_do_log(buffer, data, data_size, pfxsize, start);
263*49ef7e06SGarrett D'Amore 	if (start != pfxsize) {
264*49ef7e06SGarrett D'Amore 		buffer[start] = '\0';
265*49ef7e06SGarrett D'Amore 		cmn_err(CE_NOTE, "%s", buffer);
266*49ef7e06SGarrett D'Amore 	}
267*49ef7e06SGarrett D'Amore }
268*49ef7e06SGarrett D'Amore #endif /* EFSYS_OPT_MCDI_LOGGING */
269*49ef7e06SGarrett D'Amore 
270*49ef7e06SGarrett D'Amore int
sfxge_mcdi_init(sfxge_t * sp)271*49ef7e06SGarrett D'Amore sfxge_mcdi_init(sfxge_t *sp)
272*49ef7e06SGarrett D'Amore {
273*49ef7e06SGarrett D'Amore 	efx_nic_t *enp = sp->s_enp;
274*49ef7e06SGarrett D'Amore 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
275*49ef7e06SGarrett D'Amore 	efsys_mem_t *esmp = &(smp->sm_mem);
276*49ef7e06SGarrett D'Amore 	efx_mcdi_transport_t *emtp = &(smp->sm_emt);
277*49ef7e06SGarrett D'Amore 	sfxge_dma_buffer_attr_t dma_attr;
278*49ef7e06SGarrett D'Amore 	int msg_buf_size;
279*49ef7e06SGarrett D'Amore 	int rc;
280*49ef7e06SGarrett D'Amore 
281*49ef7e06SGarrett D'Amore 	ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_UNINITIALIZED);
282*49ef7e06SGarrett D'Amore 
283*49ef7e06SGarrett D'Amore 	msg_buf_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
284*49ef7e06SGarrett D'Amore 
285*49ef7e06SGarrett D'Amore 	/* Allocate host DMA buffer for MCDI commands */
286*49ef7e06SGarrett D'Amore 	dma_attr.sdba_dip	 = sp->s_dip;
287*49ef7e06SGarrett D'Amore 	dma_attr.sdba_dattrp	 = &sfxge_mcdi_dma_attr;
288*49ef7e06SGarrett D'Amore 	dma_attr.sdba_callback	 = DDI_DMA_SLEEP;
289*49ef7e06SGarrett D'Amore 	dma_attr.sdba_length	 = msg_buf_size;
290*49ef7e06SGarrett D'Amore 	dma_attr.sdba_memflags	 = DDI_DMA_CONSISTENT;
291*49ef7e06SGarrett D'Amore 	dma_attr.sdba_devaccp	 = &sfxge_mcdi_devacc;
292*49ef7e06SGarrett D'Amore 	dma_attr.sdba_bindflags	 = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
293*49ef7e06SGarrett D'Amore 	dma_attr.sdba_maxcookies = 1;
294*49ef7e06SGarrett D'Amore 	dma_attr.sdba_zeroinit	 = B_TRUE;
295*49ef7e06SGarrett D'Amore 
296*49ef7e06SGarrett D'Amore 	if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0)
297*49ef7e06SGarrett D'Amore 		goto fail1;
298*49ef7e06SGarrett D'Amore 
299*49ef7e06SGarrett D'Amore 	mutex_init(&(smp->sm_lock), NULL, MUTEX_DRIVER, NULL);
300*49ef7e06SGarrett D'Amore 
301*49ef7e06SGarrett D'Amore 	smp->sm_state = SFXGE_MCDI_INITIALIZED;
302*49ef7e06SGarrett D'Amore 
303*49ef7e06SGarrett D'Amore 	emtp->emt_context   = sp;
304*49ef7e06SGarrett D'Amore 	emtp->emt_dma_mem   = esmp;
305*49ef7e06SGarrett D'Amore 	emtp->emt_execute   = sfxge_mcdi_execute;
306*49ef7e06SGarrett D'Amore 	emtp->emt_ev_cpl    = sfxge_mcdi_ev_cpl;
307*49ef7e06SGarrett D'Amore 	emtp->emt_exception = sfxge_mcdi_exception;
308*49ef7e06SGarrett D'Amore #if EFSYS_OPT_MCDI_LOGGING
309*49ef7e06SGarrett D'Amore 	emtp->emt_logger    = sfxge_mcdi_logger;
310*49ef7e06SGarrett D'Amore #endif
311*49ef7e06SGarrett D'Amore 
312*49ef7e06SGarrett D'Amore 	cv_init(&(smp->sm_kv), NULL, CV_DRIVER, NULL);
313*49ef7e06SGarrett D'Amore 
314*49ef7e06SGarrett D'Amore 	if ((rc = efx_mcdi_init(enp, emtp)) != 0)
315*49ef7e06SGarrett D'Amore 		goto fail2;
316*49ef7e06SGarrett D'Amore 
317*49ef7e06SGarrett D'Amore 	return (0);
318*49ef7e06SGarrett D'Amore 
319*49ef7e06SGarrett D'Amore fail2:
320*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
321*49ef7e06SGarrett D'Amore 
322*49ef7e06SGarrett D'Amore 	cv_destroy(&(smp->sm_kv));
323*49ef7e06SGarrett D'Amore 	mutex_destroy(&(smp->sm_lock));
324*49ef7e06SGarrett D'Amore 
325*49ef7e06SGarrett D'Amore 	sfxge_dma_buffer_destroy(esmp);
326*49ef7e06SGarrett D'Amore 
327*49ef7e06SGarrett D'Amore 	smp->sm_state = SFXGE_MCDI_UNINITIALIZED;
328*49ef7e06SGarrett D'Amore 	smp->sm_sp = NULL;
329*49ef7e06SGarrett D'Amore 	SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t);
330*49ef7e06SGarrett D'Amore 
331*49ef7e06SGarrett D'Amore fail1:
332*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
333*49ef7e06SGarrett D'Amore 
334*49ef7e06SGarrett D'Amore 	return (rc);
335*49ef7e06SGarrett D'Amore }
336*49ef7e06SGarrett D'Amore 
337*49ef7e06SGarrett D'Amore 
338*49ef7e06SGarrett D'Amore void
sfxge_mcdi_fini(sfxge_t * sp)339*49ef7e06SGarrett D'Amore sfxge_mcdi_fini(sfxge_t *sp)
340*49ef7e06SGarrett D'Amore {
341*49ef7e06SGarrett D'Amore 	efx_nic_t *enp = sp->s_enp;
342*49ef7e06SGarrett D'Amore 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
343*49ef7e06SGarrett D'Amore 	efsys_mem_t *esmp = &(smp->sm_mem);
344*49ef7e06SGarrett D'Amore 	efx_mcdi_transport_t *emtp;
345*49ef7e06SGarrett D'Amore 
346*49ef7e06SGarrett D'Amore 	mutex_enter(&(smp->sm_lock));
347*49ef7e06SGarrett D'Amore 	ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_INITIALIZED);
348*49ef7e06SGarrett D'Amore 
349*49ef7e06SGarrett D'Amore 	efx_mcdi_fini(enp);
350*49ef7e06SGarrett D'Amore 	emtp = &(smp->sm_emt);
351*49ef7e06SGarrett D'Amore 	bzero(emtp, sizeof (*emtp));
352*49ef7e06SGarrett D'Amore 
353*49ef7e06SGarrett D'Amore 	smp->sm_sp = NULL;
354*49ef7e06SGarrett D'Amore 
355*49ef7e06SGarrett D'Amore 	cv_destroy(&(smp->sm_kv));
356*49ef7e06SGarrett D'Amore 	mutex_exit(&(smp->sm_lock));
357*49ef7e06SGarrett D'Amore 
358*49ef7e06SGarrett D'Amore 	sfxge_dma_buffer_destroy(esmp);
359*49ef7e06SGarrett D'Amore 
360*49ef7e06SGarrett D'Amore 	mutex_destroy(&(smp->sm_lock));
361*49ef7e06SGarrett D'Amore 
362*49ef7e06SGarrett D'Amore 	smp->sm_state = SFXGE_MCDI_UNINITIALIZED;
363*49ef7e06SGarrett D'Amore 	SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t);
364*49ef7e06SGarrett D'Amore }
365*49ef7e06SGarrett D'Amore 
366*49ef7e06SGarrett D'Amore 
367*49ef7e06SGarrett D'Amore int
sfxge_mcdi_ioctl(sfxge_t * sp,sfxge_mcdi_ioc_t * smip)368*49ef7e06SGarrett D'Amore sfxge_mcdi_ioctl(sfxge_t *sp, sfxge_mcdi_ioc_t *smip)
369*49ef7e06SGarrett D'Amore {
370*49ef7e06SGarrett D'Amore 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sp->s_enp);
371*49ef7e06SGarrett D'Amore 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
372*49ef7e06SGarrett D'Amore 	efx_mcdi_req_t emr;
373*49ef7e06SGarrett D'Amore 	uint8_t *out;
374*49ef7e06SGarrett D'Amore 	int rc;
375*49ef7e06SGarrett D'Amore 
376*49ef7e06SGarrett D'Amore 	if (smp->sm_state == SFXGE_MCDI_UNINITIALIZED) {
377*49ef7e06SGarrett D'Amore 		rc = ENODEV;
378*49ef7e06SGarrett D'Amore 		goto fail1;
379*49ef7e06SGarrett D'Amore 	}
380*49ef7e06SGarrett D'Amore 
381*49ef7e06SGarrett D'Amore 	if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
382*49ef7e06SGarrett D'Amore 		rc = ENOTSUP;
383*49ef7e06SGarrett D'Amore 		goto fail2;
384*49ef7e06SGarrett D'Amore 	}
385*49ef7e06SGarrett D'Amore 
386*49ef7e06SGarrett D'Amore 	out = kmem_zalloc(sizeof (smip->smi_payload), KM_NOSLEEP);
387*49ef7e06SGarrett D'Amore 	if (out == NULL) {
388*49ef7e06SGarrett D'Amore 		rc = ENOMEM;
389*49ef7e06SGarrett D'Amore 		goto fail3;
390*49ef7e06SGarrett D'Amore 	}
391*49ef7e06SGarrett D'Amore 
392*49ef7e06SGarrett D'Amore 	emr.emr_cmd = smip->smi_cmd;
393*49ef7e06SGarrett D'Amore 	emr.emr_in_buf = smip->smi_payload;
394*49ef7e06SGarrett D'Amore 	emr.emr_in_length = smip->smi_len;
395*49ef7e06SGarrett D'Amore 
396*49ef7e06SGarrett D'Amore 	emr.emr_out_buf = out;
397*49ef7e06SGarrett D'Amore 	emr.emr_out_length = sizeof (smip->smi_payload);
398*49ef7e06SGarrett D'Amore 
399*49ef7e06SGarrett D'Amore 	sfxge_mcdi_execute(sp, &emr);
400*49ef7e06SGarrett D'Amore 
401*49ef7e06SGarrett D'Amore 	smip->smi_rc = (uint8_t)emr.emr_rc;
402*49ef7e06SGarrett D'Amore 	smip->smi_cmd = (uint8_t)emr.emr_cmd;
403*49ef7e06SGarrett D'Amore 	smip->smi_len = (uint8_t)emr.emr_out_length_used;
404*49ef7e06SGarrett D'Amore 	bcopy(out, smip->smi_payload, smip->smi_len);
405*49ef7e06SGarrett D'Amore 
406*49ef7e06SGarrett D'Amore 	/*
407*49ef7e06SGarrett D'Amore 	 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
408*49ef7e06SGarrett D'Amore 	 * Both ports will see ->emt_exception callbacks on the next MCDI poll
409*49ef7e06SGarrett D'Amore 	 */
410*49ef7e06SGarrett D'Amore 	if (smip->smi_cmd == MC_CMD_REBOOT) {
411*49ef7e06SGarrett D'Amore 
412*49ef7e06SGarrett D'Amore 		DTRACE_PROBE(mcdi_ioctl_mc_reboot);
413*49ef7e06SGarrett D'Amore 		/* sfxge_t->s_state_lock held */
414*49ef7e06SGarrett D'Amore 		(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_OK,
415*49ef7e06SGarrett D'Amore 		    "MC_REBOOT triggering restart", 0);
416*49ef7e06SGarrett D'Amore 	}
417*49ef7e06SGarrett D'Amore 
418*49ef7e06SGarrett D'Amore 	kmem_free(out, sizeof (smip->smi_payload));
419*49ef7e06SGarrett D'Amore 
420*49ef7e06SGarrett D'Amore 	return (0);
421*49ef7e06SGarrett D'Amore 
422*49ef7e06SGarrett D'Amore fail3:
423*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail3);
424*49ef7e06SGarrett D'Amore fail2:
425*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
426*49ef7e06SGarrett D'Amore fail1:
427*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
428*49ef7e06SGarrett D'Amore 	return (rc);
429*49ef7e06SGarrett D'Amore }
430*49ef7e06SGarrett D'Amore 
431*49ef7e06SGarrett D'Amore int
sfxge_mcdi2_ioctl(sfxge_t * sp,sfxge_mcdi2_ioc_t * smip)432*49ef7e06SGarrett D'Amore sfxge_mcdi2_ioctl(sfxge_t *sp, sfxge_mcdi2_ioc_t *smip)
433*49ef7e06SGarrett D'Amore {
434*49ef7e06SGarrett D'Amore 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sp->s_enp);
435*49ef7e06SGarrett D'Amore 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
436*49ef7e06SGarrett D'Amore 	efx_mcdi_req_t emr;
437*49ef7e06SGarrett D'Amore 	uint8_t *out;
438*49ef7e06SGarrett D'Amore 	int rc;
439*49ef7e06SGarrett D'Amore 
440*49ef7e06SGarrett D'Amore 	if (smp->sm_state == SFXGE_MCDI_UNINITIALIZED) {
441*49ef7e06SGarrett D'Amore 		rc = ENODEV;
442*49ef7e06SGarrett D'Amore 		goto fail1;
443*49ef7e06SGarrett D'Amore 	}
444*49ef7e06SGarrett D'Amore 
445*49ef7e06SGarrett D'Amore 	if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
446*49ef7e06SGarrett D'Amore 		rc = ENOTSUP;
447*49ef7e06SGarrett D'Amore 		goto fail2;
448*49ef7e06SGarrett D'Amore 	}
449*49ef7e06SGarrett D'Amore 
450*49ef7e06SGarrett D'Amore 	out = kmem_zalloc(sizeof (smip->smi_payload), KM_NOSLEEP);
451*49ef7e06SGarrett D'Amore 	if (out == NULL) {
452*49ef7e06SGarrett D'Amore 		rc = ENOMEM;
453*49ef7e06SGarrett D'Amore 		goto fail3;
454*49ef7e06SGarrett D'Amore 	}
455*49ef7e06SGarrett D'Amore 
456*49ef7e06SGarrett D'Amore 	emr.emr_cmd = smip->smi_cmd;
457*49ef7e06SGarrett D'Amore 	emr.emr_in_buf = smip->smi_payload;
458*49ef7e06SGarrett D'Amore 	emr.emr_in_length = smip->smi_len;
459*49ef7e06SGarrett D'Amore 
460*49ef7e06SGarrett D'Amore 	emr.emr_out_buf = out;
461*49ef7e06SGarrett D'Amore 	emr.emr_out_length = sizeof (smip->smi_payload);
462*49ef7e06SGarrett D'Amore 
463*49ef7e06SGarrett D'Amore 	sfxge_mcdi_execute(sp, &emr);
464*49ef7e06SGarrett D'Amore 
465*49ef7e06SGarrett D'Amore 	smip->smi_rc = emr.emr_rc;
466*49ef7e06SGarrett D'Amore 	smip->smi_cmd = emr.emr_cmd;
467*49ef7e06SGarrett D'Amore 	smip->smi_len = (uint32_t)emr.emr_out_length_used;
468*49ef7e06SGarrett D'Amore 	bcopy(out, smip->smi_payload, smip->smi_len);
469*49ef7e06SGarrett D'Amore 
470*49ef7e06SGarrett D'Amore 	/*
471*49ef7e06SGarrett D'Amore 	 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
472*49ef7e06SGarrett D'Amore 	 * Both ports will see ->emt_exception callbacks on the next MCDI poll
473*49ef7e06SGarrett D'Amore 	 */
474*49ef7e06SGarrett D'Amore 	if (smip->smi_cmd == MC_CMD_REBOOT) {
475*49ef7e06SGarrett D'Amore 
476*49ef7e06SGarrett D'Amore 		DTRACE_PROBE(mcdi_ioctl_mc_reboot);
477*49ef7e06SGarrett D'Amore 		/* sfxge_t->s_state_lock held */
478*49ef7e06SGarrett D'Amore 		(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_OK,
479*49ef7e06SGarrett D'Amore 		    "MC_REBOOT triggering restart", 0);
480*49ef7e06SGarrett D'Amore 	}
481*49ef7e06SGarrett D'Amore 
482*49ef7e06SGarrett D'Amore 	kmem_free(out, sizeof (smip->smi_payload));
483*49ef7e06SGarrett D'Amore 
484*49ef7e06SGarrett D'Amore 	return (0);
485*49ef7e06SGarrett D'Amore 
486*49ef7e06SGarrett D'Amore fail3:
487*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail3);
488*49ef7e06SGarrett D'Amore fail2:
489*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
490*49ef7e06SGarrett D'Amore fail1:
491*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
492*49ef7e06SGarrett D'Amore 	return (rc);
493*49ef7e06SGarrett D'Amore }
494