2 * Copyright (c) 2009-2016 Solarflare Communications Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
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.
31 #include <sys/types.h>
33 #include <sys/sunddi.h>
34 #include <sys/stream.h>
42 #include "efx_regs_mcdi.h"
44 /* MAC DMA attributes */
45 static ddi_device_acc_attr_t sfxge_mcdi_devacc
= {
47 DDI_DEVICE_ATTR_V0
, /* devacc_attr_version */
48 DDI_NEVERSWAP_ACC
, /* devacc_attr_endian_flags */
49 DDI_STRICTORDER_ACC
/* devacc_attr_dataorder */
52 static ddi_dma_attr_t sfxge_mcdi_dma_attr
= {
53 DMA_ATTR_V0
, /* dma_attr_version */
54 0, /* dma_attr_addr_lo */
55 0xffffffffffffffffull
, /* dma_attr_addr_hi */
56 0xffffffffffffffffull
, /* dma_attr_count_max */
57 0x1000, /* dma_attr_align */
58 0xffffffff, /* dma_attr_burstsizes */
59 1, /* dma_attr_minxfer */
60 0xffffffffffffffffull
, /* dma_attr_maxxfer */
61 0xffffffffffffffffull
, /* dma_attr_seg */
62 1, /* dma_attr_sgllen */
63 1, /* dma_attr_granular */
64 0 /* dma_attr_flags */
68 * Notes on MCDI operation:
69 * ------------------------
70 * MCDI requests can be made in arbitrary thread context, and as a synchronous
71 * API must therefore block until the response is available from the MC, or
72 * a watchdog timeout occurs.
74 * This interacts badly with the limited number of worker threads (2 per CPU)
75 * used by the Solaris callout subsystem to invoke timeout handlers. If both
76 * worker threads are blocked (e.g. waiting for a condvar or mutex) then timeout
77 * processing is deadlocked on that CPU, causing system failure.
79 * For this reason the driver does not use event based MCDI completion, as this
80 * leads to numerous paths involving timeouts and reentrant GLDv3 entrypoints
81 * that result in a deadlocked system.
83 #define SFXGE_MCDI_POLL_INTERVAL 10 /* 10us in 1us units */
84 #define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */
87 /* Acquire exclusive access to MCDI for the duration of a request */
89 sfxge_mcdi_acquire(sfxge_mcdi_t
*smp
)
91 mutex_enter(&(smp
->sm_lock
));
92 ASSERT3U(smp
->sm_state
, !=, SFXGE_MCDI_UNINITIALIZED
);
94 while (smp
->sm_state
!= SFXGE_MCDI_INITIALIZED
) {
95 (void) cv_wait(&(smp
->sm_kv
), &(smp
->sm_lock
));
97 smp
->sm_state
= SFXGE_MCDI_BUSY
;
99 mutex_exit(&(smp
->sm_lock
));
103 /* Release ownership of MCDI on request completion */
105 sfxge_mcdi_release(sfxge_mcdi_t
*smp
)
107 mutex_enter(&(smp
->sm_lock
));
108 ASSERT((smp
->sm_state
== SFXGE_MCDI_BUSY
) ||
109 (smp
->sm_state
== SFXGE_MCDI_COMPLETED
));
111 smp
->sm_state
= SFXGE_MCDI_INITIALIZED
;
112 cv_broadcast(&(smp
->sm_kv
));
114 mutex_exit(&(smp
->sm_lock
));
119 sfxge_mcdi_timeout(sfxge_t
*sp
)
121 dev_info_t
*dip
= sp
->s_dip
;
123 dev_err(dip
, CE_WARN
, SFXGE_CMN_ERR
"MC_TIMEOUT");
125 DTRACE_PROBE(mcdi_timeout
);
126 (void) sfxge_restart_dispatch(sp
, DDI_SLEEP
, SFXGE_HW_ERR
,
132 sfxge_mcdi_poll(sfxge_t
*sp
)
134 efx_nic_t
*enp
= sp
->s_enp
;
138 /* Poll until request completes or timeout */
139 timeout
= ddi_get_lbolt() + drv_usectohz(SFXGE_MCDI_WATCHDOG_INTERVAL
);
140 while (efx_mcdi_request_poll(enp
) == B_FALSE
) {
142 /* No response received yet */
143 if (ddi_get_lbolt() > timeout
) {
144 /* Timeout expired */
148 /* Short delay to avoid excessive PCIe traffic */
149 drv_usecwait(SFXGE_MCDI_POLL_INTERVAL
);
152 /* Request completed (or polling failed) */
156 /* Timeout before request completion */
158 aborted
= efx_mcdi_request_abort(enp
);
160 sfxge_mcdi_timeout(sp
);
165 sfxge_mcdi_execute(void *arg
, efx_mcdi_req_t
*emrp
)
167 sfxge_t
*sp
= (sfxge_t
*)arg
;
168 sfxge_mcdi_t
*smp
= &(sp
->s_mcdi
);
170 sfxge_mcdi_acquire(smp
);
172 /* Issue request and poll for completion */
173 efx_mcdi_request_start(sp
->s_enp
, emrp
, B_FALSE
);
176 sfxge_mcdi_release(smp
);
181 sfxge_mcdi_ev_cpl(void *arg
)
183 sfxge_t
*sp
= (sfxge_t
*)arg
;
184 sfxge_mcdi_t
*smp
= &(sp
->s_mcdi
);
186 mutex_enter(&(smp
->sm_lock
));
187 ASSERT(smp
->sm_state
== SFXGE_MCDI_BUSY
);
188 smp
->sm_state
= SFXGE_MCDI_COMPLETED
;
189 cv_broadcast(&(smp
->sm_kv
));
190 mutex_exit(&(smp
->sm_lock
));
195 sfxge_mcdi_exception(void *arg
, efx_mcdi_exception_t eme
)
197 sfxge_t
*sp
= (sfxge_t
*)arg
;
200 if (eme
== EFX_MCDI_EXCEPTION_MC_REBOOT
)
201 reason
= "MC_REBOOT";
202 else if (eme
== EFX_MCDI_EXCEPTION_MC_BADASSERT
)
203 reason
= "MC_BADASSERT";
205 reason
= "MC_UNKNOWN";
207 DTRACE_PROBE(mcdi_exception
);
208 /* sfxge_evq_t->se_lock held */
209 (void) sfxge_restart_dispatch(sp
, DDI_SLEEP
, SFXGE_HW_ERR
, reason
, 0);
212 #if EFSYS_OPT_MCDI_LOGGING
213 #define SFXGE_MCDI_LOG_BUF_SIZE 128
216 sfxge_mcdi_do_log(char *buffer
, void *data
, size_t data_size
,
217 size_t pfxsize
, size_t position
)
219 uint32_t *words
= data
;
222 for (i
= 0; i
< data_size
; i
+= sizeof (*words
)) {
223 if (position
+ 2 * sizeof (*words
) + 1 >=
224 SFXGE_MCDI_LOG_BUF_SIZE
) {
225 buffer
[position
] = '\0';
226 cmn_err(CE_NOTE
, "%s \\", buffer
);
229 snprintf(buffer
+ position
, SFXGE_MCDI_LOG_BUF_SIZE
- position
,
232 position
+= 2 * sizeof (uint32_t) + 1;
239 sfxge_mcdi_logger(void *arg
, efx_log_msg_t type
,
240 void *header
, size_t header_size
, void *data
, size_t data_size
)
242 sfxge_t
*sp
= (sfxge_t
*)arg
;
243 char buffer
[SFXGE_MCDI_LOG_BUF_SIZE
];
247 if (!sp
->s_mcdi_logging
)
250 pfxsize
= snprintf(buffer
, sizeof (buffer
),
251 "sfc %04x:%02x:%02x.%02x %s%d MCDI RPC %s:",
253 PCI_REG_BUS_G(sp
->s_bus_addr
),
254 PCI_REG_DEV_G(sp
->s_bus_addr
),
255 PCI_REG_FUNC_G(sp
->s_bus_addr
),
256 ddi_driver_name(sp
->s_dip
),
257 ddi_get_instance(sp
->s_dip
),
258 type
== EFX_LOG_MCDI_REQUEST
? "REQ" :
259 type
== EFX_LOG_MCDI_RESPONSE
? "RESP" : "???");
260 start
= sfxge_mcdi_do_log(buffer
, header
, header_size
,
262 start
= sfxge_mcdi_do_log(buffer
, data
, data_size
, pfxsize
, start
);
263 if (start
!= pfxsize
) {
264 buffer
[start
] = '\0';
265 cmn_err(CE_NOTE
, "%s", buffer
);
268 #endif /* EFSYS_OPT_MCDI_LOGGING */
271 sfxge_mcdi_init(sfxge_t
*sp
)
273 efx_nic_t
*enp
= sp
->s_enp
;
274 sfxge_mcdi_t
*smp
= &(sp
->s_mcdi
);
275 efsys_mem_t
*esmp
= &(smp
->sm_mem
);
276 efx_mcdi_transport_t
*emtp
= &(smp
->sm_emt
);
277 sfxge_dma_buffer_attr_t dma_attr
;
281 ASSERT3U(smp
->sm_state
, ==, SFXGE_MCDI_UNINITIALIZED
);
283 msg_buf_size
= sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2
;
285 /* Allocate host DMA buffer for MCDI commands */
286 dma_attr
.sdba_dip
= sp
->s_dip
;
287 dma_attr
.sdba_dattrp
= &sfxge_mcdi_dma_attr
;
288 dma_attr
.sdba_callback
= DDI_DMA_SLEEP
;
289 dma_attr
.sdba_length
= msg_buf_size
;
290 dma_attr
.sdba_memflags
= DDI_DMA_CONSISTENT
;
291 dma_attr
.sdba_devaccp
= &sfxge_mcdi_devacc
;
292 dma_attr
.sdba_bindflags
= DDI_DMA_RDWR
| DDI_DMA_CONSISTENT
;
293 dma_attr
.sdba_maxcookies
= 1;
294 dma_attr
.sdba_zeroinit
= B_TRUE
;
296 if ((rc
= sfxge_dma_buffer_create(esmp
, &dma_attr
)) != 0)
299 mutex_init(&(smp
->sm_lock
), NULL
, MUTEX_DRIVER
, NULL
);
301 smp
->sm_state
= SFXGE_MCDI_INITIALIZED
;
303 emtp
->emt_context
= sp
;
304 emtp
->emt_dma_mem
= esmp
;
305 emtp
->emt_execute
= sfxge_mcdi_execute
;
306 emtp
->emt_ev_cpl
= sfxge_mcdi_ev_cpl
;
307 emtp
->emt_exception
= sfxge_mcdi_exception
;
308 #if EFSYS_OPT_MCDI_LOGGING
309 emtp
->emt_logger
= sfxge_mcdi_logger
;
312 cv_init(&(smp
->sm_kv
), NULL
, CV_DRIVER
, NULL
);
314 if ((rc
= efx_mcdi_init(enp
, emtp
)) != 0)
322 cv_destroy(&(smp
->sm_kv
));
323 mutex_destroy(&(smp
->sm_lock
));
325 sfxge_dma_buffer_destroy(esmp
);
327 smp
->sm_state
= SFXGE_MCDI_UNINITIALIZED
;
329 SFXGE_OBJ_CHECK(smp
, sfxge_mcdi_t
);
332 DTRACE_PROBE1(fail1
, int, rc
);
339 sfxge_mcdi_fini(sfxge_t
*sp
)
341 efx_nic_t
*enp
= sp
->s_enp
;
342 sfxge_mcdi_t
*smp
= &(sp
->s_mcdi
);
343 efsys_mem_t
*esmp
= &(smp
->sm_mem
);
344 efx_mcdi_transport_t
*emtp
;
346 mutex_enter(&(smp
->sm_lock
));
347 ASSERT3U(smp
->sm_state
, ==, SFXGE_MCDI_INITIALIZED
);
350 emtp
= &(smp
->sm_emt
);
351 bzero(emtp
, sizeof (*emtp
));
355 cv_destroy(&(smp
->sm_kv
));
356 mutex_exit(&(smp
->sm_lock
));
358 sfxge_dma_buffer_destroy(esmp
);
360 mutex_destroy(&(smp
->sm_lock
));
362 smp
->sm_state
= SFXGE_MCDI_UNINITIALIZED
;
363 SFXGE_OBJ_CHECK(smp
, sfxge_mcdi_t
);
368 sfxge_mcdi_ioctl(sfxge_t
*sp
, sfxge_mcdi_ioc_t
*smip
)
370 const efx_nic_cfg_t
*encp
= efx_nic_cfg_get(sp
->s_enp
);
371 sfxge_mcdi_t
*smp
= &(sp
->s_mcdi
);
376 if (smp
->sm_state
== SFXGE_MCDI_UNINITIALIZED
) {
381 if (!(encp
->enc_features
& EFX_FEATURE_MCDI
)) {
386 out
= kmem_zalloc(sizeof (smip
->smi_payload
), KM_NOSLEEP
);
392 emr
.emr_cmd
= smip
->smi_cmd
;
393 emr
.emr_in_buf
= smip
->smi_payload
;
394 emr
.emr_in_length
= smip
->smi_len
;
396 emr
.emr_out_buf
= out
;
397 emr
.emr_out_length
= sizeof (smip
->smi_payload
);
399 sfxge_mcdi_execute(sp
, &emr
);
401 smip
->smi_rc
= (uint8_t)emr
.emr_rc
;
402 smip
->smi_cmd
= (uint8_t)emr
.emr_cmd
;
403 smip
->smi_len
= (uint8_t)emr
.emr_out_length_used
;
404 bcopy(out
, smip
->smi_payload
, smip
->smi_len
);
407 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
408 * Both ports will see ->emt_exception callbacks on the next MCDI poll
410 if (smip
->smi_cmd
== MC_CMD_REBOOT
) {
412 DTRACE_PROBE(mcdi_ioctl_mc_reboot
);
413 /* sfxge_t->s_state_lock held */
414 (void) sfxge_restart_dispatch(sp
, DDI_SLEEP
, SFXGE_HW_OK
,
415 "MC_REBOOT triggering restart", 0);
418 kmem_free(out
, sizeof (smip
->smi_payload
));
427 DTRACE_PROBE1(fail1
, int, rc
);
432 sfxge_mcdi2_ioctl(sfxge_t
*sp
, sfxge_mcdi2_ioc_t
*smip
)
434 const efx_nic_cfg_t
*encp
= efx_nic_cfg_get(sp
->s_enp
);
435 sfxge_mcdi_t
*smp
= &(sp
->s_mcdi
);
440 if (smp
->sm_state
== SFXGE_MCDI_UNINITIALIZED
) {
445 if (!(encp
->enc_features
& EFX_FEATURE_MCDI
)) {
450 out
= kmem_zalloc(sizeof (smip
->smi_payload
), KM_NOSLEEP
);
456 emr
.emr_cmd
= smip
->smi_cmd
;
457 emr
.emr_in_buf
= smip
->smi_payload
;
458 emr
.emr_in_length
= smip
->smi_len
;
460 emr
.emr_out_buf
= out
;
461 emr
.emr_out_length
= sizeof (smip
->smi_payload
);
463 sfxge_mcdi_execute(sp
, &emr
);
465 smip
->smi_rc
= emr
.emr_rc
;
466 smip
->smi_cmd
= emr
.emr_cmd
;
467 smip
->smi_len
= (uint32_t)emr
.emr_out_length_used
;
468 bcopy(out
, smip
->smi_payload
, smip
->smi_len
);
471 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
472 * Both ports will see ->emt_exception callbacks on the next MCDI poll
474 if (smip
->smi_cmd
== MC_CMD_REBOOT
) {
476 DTRACE_PROBE(mcdi_ioctl_mc_reboot
);
477 /* sfxge_t->s_state_lock held */
478 (void) sfxge_restart_dispatch(sp
, DDI_SLEEP
, SFXGE_HW_OK
,
479 "MC_REBOOT triggering restart", 0);
482 kmem_free(out
, sizeof (smip
->smi_payload
));
491 DTRACE_PROBE1(fail1
, int, rc
);