1227569Sphilip/*- 2284555Sarybchik * Copyright (c) 2008-2015 Solarflare Communications Inc. 3284555Sarybchik * All rights reserved. 4227569Sphilip * 5227569Sphilip * Redistribution and use in source and binary forms, with or without 6284555Sarybchik * modification, are permitted provided that the following conditions are met: 7227569Sphilip * 8284555Sarybchik * 1. Redistributions of source code must retain the above copyright notice, 9284555Sarybchik * this list of conditions and the following disclaimer. 10284555Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice, 11284555Sarybchik * this list of conditions and the following disclaimer in the documentation 12284555Sarybchik * and/or other materials provided with the distribution. 13284555Sarybchik * 14284555Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15284555Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16284555Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17284555Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18284555Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19284555Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20284555Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21284555Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22284555Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23284555Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24284555Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25284555Sarybchik * 26284555Sarybchik * The views and conclusions contained in the software and documentation are 27284555Sarybchik * those of the authors and should not be interpreted as representing official 28284555Sarybchik * policies, either expressed or implied, of the FreeBSD Project. 29227569Sphilip */ 30227569Sphilip 31228078Sphilip#include <sys/cdefs.h> 32228078Sphilip__FBSDID("$FreeBSD: releng/10.2/sys/dev/sfxge/common/efx_mcdi.c 284555 2015-06-18 15:46:39Z arybchik $"); 33228078Sphilip 34227569Sphilip#include "efsys.h" 35227569Sphilip#include "efx.h" 36227569Sphilip#include "efx_types.h" 37227569Sphilip#include "efx_regs.h" 38227569Sphilip#include "efx_regs_mcdi.h" 39227569Sphilip#include "efx_impl.h" 40227569Sphilip 41227569Sphilip#if EFSYS_OPT_MCDI 42227569Sphilip 43227569Sphilip 44284555Sarybchik#if EFSYS_OPT_SIENA 45284555Sarybchik 46284555Sarybchikstatic efx_mcdi_ops_t __efx_mcdi_siena_ops = { 47284555Sarybchik siena_mcdi_init, /* emco_init */ 48284555Sarybchik siena_mcdi_request_copyin, /* emco_request_copyin */ 49284555Sarybchik siena_mcdi_request_poll, /* emco_request_poll */ 50284555Sarybchik siena_mcdi_request_copyout, /* emco_request_copyout */ 51284555Sarybchik siena_mcdi_poll_reboot, /* emco_poll_reboot */ 52284555Sarybchik siena_mcdi_fini, /* emco_fini */ 53284555Sarybchik siena_mcdi_fw_update_supported, /* emco_fw_update_supported */ 54284555Sarybchik siena_mcdi_macaddr_change_supported, 55284555Sarybchik /* emco_macaddr_change_supported */ 56284555Sarybchik}; 57284555Sarybchik 58284555Sarybchik#endif /* EFSYS_OPT_SIENA */ 59284555Sarybchik 60284555Sarybchik#if EFSYS_OPT_HUNTINGTON 61284555Sarybchik 62284555Sarybchikstatic efx_mcdi_ops_t __efx_mcdi_hunt_ops = { 63284555Sarybchik hunt_mcdi_init, /* emco_init */ 64284555Sarybchik hunt_mcdi_request_copyin, /* emco_request_copyin */ 65284555Sarybchik hunt_mcdi_request_poll, /* emco_request_poll */ 66284555Sarybchik hunt_mcdi_request_copyout, /* emco_request_copyout */ 67284555Sarybchik hunt_mcdi_poll_reboot, /* emco_poll_reboot */ 68284555Sarybchik hunt_mcdi_fini, /* emco_fini */ 69284555Sarybchik hunt_mcdi_fw_update_supported, /* emco_fw_update_supported */ 70284555Sarybchik hunt_mcdi_macaddr_change_supported, 71284555Sarybchik /* emco_macaddr_change_supported */ 72284555Sarybchik}; 73284555Sarybchik 74284555Sarybchik#endif /* EFSYS_OPT_HUNTINGTON */ 75284555Sarybchik 76284555Sarybchik 77284555Sarybchik 78284555Sarybchik __checkReturn int 79284555Sarybchikefx_mcdi_init( 80284555Sarybchik __in efx_nic_t *enp, 81284555Sarybchik __in const efx_mcdi_transport_t *emtp) 82284555Sarybchik{ 83284555Sarybchik efx_mcdi_ops_t *emcop; 84284555Sarybchik int rc; 85284555Sarybchik 86284555Sarybchik EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 87284555Sarybchik EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0); 88284555Sarybchik 89284555Sarybchik switch (enp->en_family) { 90284555Sarybchik#if EFSYS_OPT_FALCON 91284555Sarybchik case EFX_FAMILY_FALCON: 92284555Sarybchik emcop = NULL; 93284555Sarybchik emtp = NULL; 94284555Sarybchik break; 95284555Sarybchik#endif /* EFSYS_OPT_FALCON */ 96284555Sarybchik 97284555Sarybchik#if EFSYS_OPT_SIENA 98284555Sarybchik case EFX_FAMILY_SIENA: 99284555Sarybchik emcop = (efx_mcdi_ops_t *)&__efx_mcdi_siena_ops; 100284555Sarybchik break; 101284555Sarybchik#endif /* EFSYS_OPT_SIENA */ 102284555Sarybchik 103284555Sarybchik#if EFSYS_OPT_HUNTINGTON 104284555Sarybchik case EFX_FAMILY_HUNTINGTON: 105284555Sarybchik emcop = (efx_mcdi_ops_t *)&__efx_mcdi_hunt_ops; 106284555Sarybchik break; 107284555Sarybchik#endif /* EFSYS_OPT_HUNTINGTON */ 108284555Sarybchik 109284555Sarybchik default: 110284555Sarybchik EFSYS_ASSERT(0); 111284555Sarybchik rc = ENOTSUP; 112284555Sarybchik goto fail1; 113284555Sarybchik } 114284555Sarybchik 115284555Sarybchik if (enp->en_features & EFX_FEATURE_MCDI_DMA) { 116284555Sarybchik /* MCDI requires a DMA buffer in host memory */ 117284555Sarybchik if ((emtp == NULL) || (emtp->emt_dma_mem) == NULL) { 118284555Sarybchik rc = EINVAL; 119284555Sarybchik goto fail2; 120284555Sarybchik } 121284555Sarybchik } 122284555Sarybchik enp->en_mcdi.em_emtp = emtp; 123284555Sarybchik 124284555Sarybchik if (emcop != NULL && emcop->emco_init != NULL) { 125284555Sarybchik if ((rc = emcop->emco_init(enp, emtp)) != 0) 126284555Sarybchik goto fail3; 127284555Sarybchik } 128284555Sarybchik 129284555Sarybchik enp->en_mcdi.em_emcop = emcop; 130284555Sarybchik enp->en_mod_flags |= EFX_MOD_MCDI; 131284555Sarybchik 132284555Sarybchik return (0); 133284555Sarybchik 134284555Sarybchikfail3: 135284555Sarybchik EFSYS_PROBE(fail3); 136284555Sarybchikfail2: 137284555Sarybchik EFSYS_PROBE(fail2); 138284555Sarybchikfail1: 139284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 140284555Sarybchik 141284555Sarybchik enp->en_mcdi.em_emcop = NULL; 142284555Sarybchik enp->en_mcdi.em_emtp = NULL; 143284555Sarybchik enp->en_mod_flags &= ~EFX_MOD_MCDI; 144284555Sarybchik 145284555Sarybchik return (rc); 146284555Sarybchik} 147284555Sarybchik 148227569Sphilip void 149284555Sarybchikefx_mcdi_fini( 150284555Sarybchik __in efx_nic_t *enp) 151284555Sarybchik{ 152284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 153284555Sarybchik efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop; 154284555Sarybchik 155284555Sarybchik EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 156284555Sarybchik EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI); 157284555Sarybchik 158284555Sarybchik if (emcop != NULL && emcop->emco_fini != NULL) 159284555Sarybchik emcop->emco_fini(enp); 160284555Sarybchik 161284555Sarybchik emip->emi_port = 0; 162284555Sarybchik emip->emi_aborted = 0; 163284555Sarybchik 164284555Sarybchik enp->en_mcdi.em_emcop = NULL; 165284555Sarybchik enp->en_mod_flags &= ~EFX_MOD_MCDI; 166284555Sarybchik} 167284555Sarybchik 168284555Sarybchik void 169284555Sarybchikefx_mcdi_new_epoch( 170284555Sarybchik __in efx_nic_t *enp) 171284555Sarybchik{ 172284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 173284555Sarybchik int state; 174284555Sarybchik 175284555Sarybchik /* Start a new epoch (allow fresh MCDI requests to succeed) */ 176284555Sarybchik EFSYS_LOCK(enp->en_eslp, state); 177284555Sarybchik emip->emi_new_epoch = B_TRUE; 178284555Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 179284555Sarybchik} 180284555Sarybchik 181284555Sarybchik 182284555Sarybchik void 183227569Sphilipefx_mcdi_request_start( 184227569Sphilip __in efx_nic_t *enp, 185227569Sphilip __in efx_mcdi_req_t *emrp, 186227569Sphilip __in boolean_t ev_cpl) 187227569Sphilip{ 188284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 189284555Sarybchik efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop; 190227569Sphilip unsigned int seq; 191284555Sarybchik boolean_t new_epoch; 192227569Sphilip int state; 193227569Sphilip 194227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 195227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 196227569Sphilip EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 197227569Sphilip 198284555Sarybchik if (emcop == NULL || emcop->emco_request_copyin == NULL) 199284555Sarybchik return; 200227569Sphilip 201227569Sphilip /* 202227569Sphilip * efx_mcdi_request_start() is naturally serialised against both 203227569Sphilip * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(), 204250460Seadler * by virtue of there only being one outstanding MCDI request. 205227569Sphilip * Unfortunately, upper layers may also call efx_mcdi_request_abort() 206227569Sphilip * at any time, to timeout a pending mcdi request, That request may 207227569Sphilip * then subsequently complete, meaning efx_mcdi_ev_cpl() or 208227569Sphilip * efx_mcdi_ev_death() may end up running in parallel with 209227569Sphilip * efx_mcdi_request_start(). This race is handled by ensuring that 210227569Sphilip * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the 211227569Sphilip * en_eslp lock. 212227569Sphilip */ 213227569Sphilip EFSYS_LOCK(enp->en_eslp, state); 214227569Sphilip EFSYS_ASSERT(emip->emi_pending_req == NULL); 215227569Sphilip emip->emi_pending_req = emrp; 216227569Sphilip emip->emi_ev_cpl = ev_cpl; 217227569Sphilip emip->emi_poll_cnt = 0; 218284555Sarybchik seq = emip->emi_seq++ & EFX_MASK32(MCDI_HEADER_SEQ); 219284555Sarybchik new_epoch = emip->emi_new_epoch; 220227569Sphilip EFSYS_UNLOCK(enp->en_eslp, state); 221227569Sphilip 222284555Sarybchik emcop->emco_request_copyin(enp, emrp, seq, ev_cpl, new_epoch); 223284555Sarybchik} 224227569Sphilip 225284555Sarybchik __checkReturn boolean_t 226284555Sarybchikefx_mcdi_request_poll( 227284555Sarybchik __in efx_nic_t *enp) 228284555Sarybchik{ 229284555Sarybchik efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop; 230284555Sarybchik boolean_t completed; 231227569Sphilip 232284555Sarybchik EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 233284555Sarybchik EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 234284555Sarybchik EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 235227569Sphilip 236284555Sarybchik completed = B_FALSE; 237284555Sarybchik 238284555Sarybchik if (emcop != NULL && emcop->emco_request_poll != NULL) 239284555Sarybchik completed = emcop->emco_request_poll(enp); 240284555Sarybchik 241284555Sarybchik return (completed); 242227569Sphilip} 243227569Sphilip 244284555Sarybchik __checkReturn boolean_t 245284555Sarybchikefx_mcdi_request_abort( 246284555Sarybchik __in efx_nic_t *enp) 247227569Sphilip{ 248284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 249284555Sarybchik efx_mcdi_req_t *emrp; 250284555Sarybchik boolean_t aborted; 251284555Sarybchik int state; 252227569Sphilip 253284555Sarybchik EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 254284555Sarybchik EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 255284555Sarybchik EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 256227569Sphilip 257284555Sarybchik /* 258284555Sarybchik * efx_mcdi_ev_* may have already completed this event, and be 259284555Sarybchik * spinning/blocked on the upper layer lock. So it *is* legitimate 260284555Sarybchik * to for emi_pending_req to be NULL. If there is a pending event 261284555Sarybchik * completed request, then provide a "credit" to allow 262284555Sarybchik * efx_mcdi_ev_cpl() to accept a single spurious completion. 263284555Sarybchik */ 264284555Sarybchik EFSYS_LOCK(enp->en_eslp, state); 265284555Sarybchik emrp = emip->emi_pending_req; 266284555Sarybchik aborted = (emrp != NULL); 267284555Sarybchik if (aborted) { 268284555Sarybchik emip->emi_pending_req = NULL; 269284555Sarybchik 270284555Sarybchik /* Error the request */ 271284555Sarybchik emrp->emr_out_length_used = 0; 272284555Sarybchik emrp->emr_rc = ETIMEDOUT; 273284555Sarybchik 274284555Sarybchik /* Provide a credit for seqno/emr_pending_req mismatches */ 275284555Sarybchik if (emip->emi_ev_cpl) 276284555Sarybchik ++emip->emi_aborted; 277284555Sarybchik 278284555Sarybchik /* 279284555Sarybchik * The upper layer has called us, so we don't 280284555Sarybchik * need to complete the request. 281284555Sarybchik */ 282227569Sphilip } 283284555Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 284284555Sarybchik 285284555Sarybchik return (aborted); 286227569Sphilip} 287227569Sphilip 288284555Sarybchik __checkReturn int 289227569Sphilipefx_mcdi_request_errcode( 290227569Sphilip __in unsigned int err) 291227569Sphilip{ 292227569Sphilip 293227569Sphilip switch (err) { 294284555Sarybchik /* MCDI v1 */ 295284555Sarybchik case MC_CMD_ERR_EPERM: 296284555Sarybchik return (EACCES); 297227569Sphilip case MC_CMD_ERR_ENOENT: 298227569Sphilip return (ENOENT); 299227569Sphilip case MC_CMD_ERR_EINTR: 300227569Sphilip return (EINTR); 301227569Sphilip case MC_CMD_ERR_EACCES: 302227569Sphilip return (EACCES); 303227569Sphilip case MC_CMD_ERR_EBUSY: 304227569Sphilip return (EBUSY); 305227569Sphilip case MC_CMD_ERR_EINVAL: 306227569Sphilip return (EINVAL); 307227569Sphilip case MC_CMD_ERR_EDEADLK: 308227569Sphilip return (EDEADLK); 309227569Sphilip case MC_CMD_ERR_ENOSYS: 310227569Sphilip return (ENOTSUP); 311227569Sphilip case MC_CMD_ERR_ETIME: 312227569Sphilip return (ETIMEDOUT); 313284555Sarybchik case MC_CMD_ERR_ENOTSUP: 314284555Sarybchik return (ENOTSUP); 315284555Sarybchik case MC_CMD_ERR_EALREADY: 316284555Sarybchik return (EALREADY); 317284555Sarybchik 318284555Sarybchik /* MCDI v2 */ 319284555Sarybchik#ifdef MC_CMD_ERR_EAGAIN 320227569Sphilip case MC_CMD_ERR_EAGAIN: 321227569Sphilip return (EAGAIN); 322284555Sarybchik#endif 323284555Sarybchik#ifdef MC_CMD_ERR_ENOSPC 324227569Sphilip case MC_CMD_ERR_ENOSPC: 325227569Sphilip return (ENOSPC); 326227569Sphilip#endif 327284555Sarybchik 328284555Sarybchik case MC_CMD_ERR_ALLOC_FAIL: 329284555Sarybchik return (ENOMEM); 330284555Sarybchik case MC_CMD_ERR_NO_VADAPTOR: 331284555Sarybchik return (ENOENT); 332284555Sarybchik case MC_CMD_ERR_NO_EVB_PORT: 333284555Sarybchik return (ENOENT); 334284555Sarybchik case MC_CMD_ERR_NO_VSWITCH: 335284555Sarybchik return (ENODEV); 336284555Sarybchik case MC_CMD_ERR_VLAN_LIMIT: 337284555Sarybchik return (EINVAL); 338284555Sarybchik case MC_CMD_ERR_BAD_PCI_FUNC: 339284555Sarybchik return (ENODEV); 340284555Sarybchik case MC_CMD_ERR_BAD_VLAN_MODE: 341284555Sarybchik return (EINVAL); 342284555Sarybchik case MC_CMD_ERR_BAD_VSWITCH_TYPE: 343284555Sarybchik return (EINVAL); 344284555Sarybchik case MC_CMD_ERR_BAD_VPORT_TYPE: 345284555Sarybchik return (EINVAL); 346284555Sarybchik case MC_CMD_ERR_MAC_EXIST: 347284555Sarybchik return (EEXIST); 348284555Sarybchik 349227569Sphilip default: 350227569Sphilip EFSYS_PROBE1(mc_pcol_error, int, err); 351227569Sphilip return (EIO); 352227569Sphilip } 353227569Sphilip} 354227569Sphilip 355284555Sarybchik void 356227569Sphilipefx_mcdi_raise_exception( 357227569Sphilip __in efx_nic_t *enp, 358227569Sphilip __in_opt efx_mcdi_req_t *emrp, 359227569Sphilip __in int rc) 360227569Sphilip{ 361284555Sarybchik const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 362227569Sphilip efx_mcdi_exception_t exception; 363227569Sphilip 364227569Sphilip /* Reboot or Assertion failure only */ 365227569Sphilip EFSYS_ASSERT(rc == EIO || rc == EINTR); 366227569Sphilip 367227569Sphilip /* 368227569Sphilip * If MC_CMD_REBOOT causes a reboot (dependent on parameters), 369227569Sphilip * then the EIO is not worthy of an exception. 370227569Sphilip */ 371227569Sphilip if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO) 372227569Sphilip return; 373227569Sphilip 374227569Sphilip exception = (rc == EIO) 375227569Sphilip ? EFX_MCDI_EXCEPTION_MC_REBOOT 376227569Sphilip : EFX_MCDI_EXCEPTION_MC_BADASSERT; 377227569Sphilip 378227569Sphilip emtp->emt_exception(emtp->emt_context, exception); 379227569Sphilip} 380227569Sphilip 381227569Sphilipstatic int 382227569Sphilipefx_mcdi_poll_reboot( 383227569Sphilip __in efx_nic_t *enp) 384227569Sphilip{ 385284555Sarybchik efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop; 386227569Sphilip 387284555Sarybchik return (emcop->emco_poll_reboot(enp)); 388284555Sarybchik} 389227569Sphilip 390227569Sphilip 391284555Sarybchik void 392284555Sarybchikefx_mcdi_execute( 393284555Sarybchik __in efx_nic_t *enp, 394284555Sarybchik __inout efx_mcdi_req_t *emrp) 395227569Sphilip{ 396284555Sarybchik const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 397227569Sphilip 398227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 399227569Sphilip EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 400227569Sphilip 401284555Sarybchik emrp->emr_quiet = B_FALSE; 402284555Sarybchik emtp->emt_execute(emtp->emt_context, emrp); 403227569Sphilip} 404227569Sphilip 405227569Sphilip void 406284555Sarybchikefx_mcdi_execute_quiet( 407227569Sphilip __in efx_nic_t *enp, 408284555Sarybchik __inout efx_mcdi_req_t *emrp) 409227569Sphilip{ 410284555Sarybchik const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 411227569Sphilip 412227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 413227569Sphilip EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 414227569Sphilip 415284555Sarybchik emrp->emr_quiet = B_TRUE; 416227569Sphilip emtp->emt_execute(emtp->emt_context, emrp); 417227569Sphilip} 418227569Sphilip 419227569Sphilip void 420227569Sphilipefx_mcdi_ev_cpl( 421227569Sphilip __in efx_nic_t *enp, 422227569Sphilip __in unsigned int seq, 423227569Sphilip __in unsigned int outlen, 424227569Sphilip __in int errcode) 425227569Sphilip{ 426284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 427284555Sarybchik const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 428284555Sarybchik efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop; 429227569Sphilip efx_mcdi_req_t *emrp; 430227569Sphilip int state; 431227569Sphilip 432227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 433227569Sphilip EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 434227569Sphilip 435227569Sphilip /* 436227569Sphilip * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start() 437227569Sphilip * when we're completing an aborted request. 438227569Sphilip */ 439227569Sphilip EFSYS_LOCK(enp->en_eslp, state); 440227569Sphilip if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl || 441284555Sarybchik (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) { 442227569Sphilip EFSYS_ASSERT(emip->emi_aborted > 0); 443227569Sphilip if (emip->emi_aborted > 0) 444227569Sphilip --emip->emi_aborted; 445227569Sphilip EFSYS_UNLOCK(enp->en_eslp, state); 446227569Sphilip return; 447227569Sphilip } 448227569Sphilip 449227569Sphilip emrp = emip->emi_pending_req; 450227569Sphilip emip->emi_pending_req = NULL; 451227569Sphilip EFSYS_UNLOCK(enp->en_eslp, state); 452227569Sphilip 453227569Sphilip /* 454227569Sphilip * Fill out the remaining hdr fields, and copyout the payload 455227569Sphilip * if the user supplied an output buffer. 456227569Sphilip */ 457227569Sphilip if (errcode != 0) { 458284555Sarybchik if (!emrp->emr_quiet) { 459284555Sarybchik EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, 460284555Sarybchik int, errcode); 461284555Sarybchik } 462227569Sphilip emrp->emr_out_length_used = 0; 463227569Sphilip emrp->emr_rc = efx_mcdi_request_errcode(errcode); 464227569Sphilip } else { 465227569Sphilip emrp->emr_out_length_used = outlen; 466227569Sphilip emrp->emr_rc = 0; 467284555Sarybchik 468284555Sarybchik emcop->emco_request_copyout(enp, emrp); 469227569Sphilip } 470227569Sphilip 471227569Sphilip emtp->emt_ev_cpl(emtp->emt_context); 472227569Sphilip} 473227569Sphilip 474227569Sphilip void 475227569Sphilipefx_mcdi_ev_death( 476227569Sphilip __in efx_nic_t *enp, 477227569Sphilip __in int rc) 478227569Sphilip{ 479284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 480284555Sarybchik const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 481227569Sphilip efx_mcdi_req_t *emrp = NULL; 482227569Sphilip boolean_t ev_cpl; 483227569Sphilip int state; 484227569Sphilip 485227569Sphilip /* 486227569Sphilip * The MCDI request (if there is one) has been terminated, either 487227569Sphilip * by a BADASSERT or REBOOT event. 488227569Sphilip * 489250460Seadler * If there is an outstanding event-completed MCDI operation, then we 490227569Sphilip * will never receive the completion event (because both MCDI 491227569Sphilip * completions and BADASSERT events are sent to the same evq). So 492227569Sphilip * complete this MCDI op. 493227569Sphilip * 494227569Sphilip * This function might run in parallel with efx_mcdi_request_poll() 495227569Sphilip * for poll completed mcdi requests, and also with 496227569Sphilip * efx_mcdi_request_start() for post-watchdog completions. 497227569Sphilip */ 498227569Sphilip EFSYS_LOCK(enp->en_eslp, state); 499227569Sphilip emrp = emip->emi_pending_req; 500227569Sphilip ev_cpl = emip->emi_ev_cpl; 501227569Sphilip if (emrp != NULL && emip->emi_ev_cpl) { 502227569Sphilip emip->emi_pending_req = NULL; 503227569Sphilip 504227569Sphilip emrp->emr_out_length_used = 0; 505227569Sphilip emrp->emr_rc = rc; 506227569Sphilip ++emip->emi_aborted; 507227569Sphilip } 508227569Sphilip 509280535Sarybchik /* 510280535Sarybchik * Since we're running in parallel with a request, consume the 511227569Sphilip * status word before dropping the lock. 512227569Sphilip */ 513227569Sphilip if (rc == EIO || rc == EINTR) { 514284555Sarybchik EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US); 515227569Sphilip (void) efx_mcdi_poll_reboot(enp); 516284555Sarybchik emip->emi_new_epoch = B_TRUE; 517227569Sphilip } 518227569Sphilip 519227569Sphilip EFSYS_UNLOCK(enp->en_eslp, state); 520227569Sphilip 521227569Sphilip efx_mcdi_raise_exception(enp, emrp, rc); 522227569Sphilip 523227569Sphilip if (emrp != NULL && ev_cpl) 524227569Sphilip emtp->emt_ev_cpl(emtp->emt_context); 525227569Sphilip} 526227569Sphilip 527227569Sphilip __checkReturn int 528227569Sphilipefx_mcdi_version( 529227569Sphilip __in efx_nic_t *enp, 530227569Sphilip __out_ecount_opt(4) uint16_t versionp[4], 531227569Sphilip __out_opt uint32_t *buildp, 532227569Sphilip __out_opt efx_mcdi_boot_t *statusp) 533227569Sphilip{ 534227569Sphilip efx_mcdi_req_t req; 535284555Sarybchik uint8_t payload[MAX(MAX(MC_CMD_GET_VERSION_IN_LEN, 536284555Sarybchik MC_CMD_GET_VERSION_OUT_LEN), 537284555Sarybchik MAX(MC_CMD_GET_BOOT_STATUS_IN_LEN, 538284555Sarybchik MC_CMD_GET_BOOT_STATUS_OUT_LEN))]; 539227569Sphilip efx_word_t *ver_words; 540227569Sphilip uint16_t version[4]; 541227569Sphilip uint32_t build; 542227569Sphilip efx_mcdi_boot_t status; 543227569Sphilip int rc; 544227569Sphilip 545227569Sphilip EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 546227569Sphilip 547284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 548227569Sphilip req.emr_cmd = MC_CMD_GET_VERSION; 549284555Sarybchik req.emr_in_buf = payload; 550284555Sarybchik req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN; 551284555Sarybchik req.emr_out_buf = payload; 552227569Sphilip req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN; 553227569Sphilip 554227569Sphilip efx_mcdi_execute(enp, &req); 555227569Sphilip 556227569Sphilip if (req.emr_rc != 0) { 557227569Sphilip rc = req.emr_rc; 558227569Sphilip goto fail1; 559227569Sphilip } 560227569Sphilip 561227569Sphilip /* bootrom support */ 562227569Sphilip if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) { 563227569Sphilip version[0] = version[1] = version[2] = version[3] = 0; 564227569Sphilip build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE); 565227569Sphilip 566227569Sphilip goto version; 567227569Sphilip } 568227569Sphilip 569227569Sphilip if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) { 570227569Sphilip rc = EMSGSIZE; 571227569Sphilip goto fail2; 572227569Sphilip } 573227569Sphilip 574227569Sphilip ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION); 575227569Sphilip version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0); 576227569Sphilip version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0); 577227569Sphilip version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0); 578227569Sphilip version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0); 579227569Sphilip build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE); 580227569Sphilip 581227569Sphilipversion: 582227569Sphilip /* The bootrom doesn't understand BOOT_STATUS */ 583284555Sarybchik if (MC_FW_VERSION_IS_BOOTLOADER(build)) { 584227569Sphilip status = EFX_MCDI_BOOT_ROM; 585227569Sphilip goto out; 586227569Sphilip } 587227569Sphilip 588284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 589227569Sphilip req.emr_cmd = MC_CMD_GET_BOOT_STATUS; 590284555Sarybchik req.emr_in_buf = payload; 591284555Sarybchik req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN; 592284555Sarybchik req.emr_out_buf = payload; 593227569Sphilip req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN; 594227569Sphilip 595284555Sarybchik efx_mcdi_execute_quiet(enp, &req); 596227569Sphilip 597284555Sarybchik if (req.emr_rc == EACCES) { 598284555Sarybchik /* Unprivileged functions cannot access BOOT_STATUS */ 599284555Sarybchik status = EFX_MCDI_BOOT_PRIMARY; 600284555Sarybchik version[0] = version[1] = version[2] = version[3] = 0; 601284555Sarybchik build = 0; 602284555Sarybchik goto out; 603284555Sarybchik } 604284555Sarybchik 605227569Sphilip if (req.emr_rc != 0) { 606227569Sphilip rc = req.emr_rc; 607227569Sphilip goto fail3; 608227569Sphilip } 609227569Sphilip 610227569Sphilip if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) { 611227569Sphilip rc = EMSGSIZE; 612227569Sphilip goto fail4; 613227569Sphilip } 614227569Sphilip 615227569Sphilip if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS, 616227569Sphilip GET_BOOT_STATUS_OUT_FLAGS_PRIMARY)) 617227569Sphilip status = EFX_MCDI_BOOT_PRIMARY; 618227569Sphilip else 619227569Sphilip status = EFX_MCDI_BOOT_SECONDARY; 620227569Sphilip 621227569Sphilipout: 622227569Sphilip if (versionp != NULL) 623227569Sphilip memcpy(versionp, version, sizeof (version)); 624227569Sphilip if (buildp != NULL) 625227569Sphilip *buildp = build; 626227569Sphilip if (statusp != NULL) 627227569Sphilip *statusp = status; 628227569Sphilip 629227569Sphilip return (0); 630227569Sphilip 631227569Sphilipfail4: 632227569Sphilip EFSYS_PROBE(fail4); 633227569Sphilipfail3: 634227569Sphilip EFSYS_PROBE(fail3); 635227569Sphilipfail2: 636227569Sphilip EFSYS_PROBE(fail2); 637227569Sphilipfail1: 638227569Sphilip EFSYS_PROBE1(fail1, int, rc); 639227569Sphilip 640227569Sphilip return (rc); 641227569Sphilip} 642227569Sphilip 643284555Sarybchikstatic __checkReturn int 644284555Sarybchikefx_mcdi_do_reboot( 645227569Sphilip __in efx_nic_t *enp, 646284555Sarybchik __in boolean_t after_assertion) 647227569Sphilip{ 648284555Sarybchik uint8_t payload[MAX(MC_CMD_REBOOT_IN_LEN, MC_CMD_REBOOT_OUT_LEN)]; 649284555Sarybchik efx_mcdi_req_t req; 650227569Sphilip int rc; 651227569Sphilip 652284555Sarybchik /* 653284555Sarybchik * We could require the caller to have caused en_mod_flags=0 to 654284555Sarybchik * call this function. This doesn't help the other port though, 655284555Sarybchik * who's about to get the MC ripped out from underneath them. 656284555Sarybchik * Since they have to cope with the subsequent fallout of MCDI 657284555Sarybchik * failures, we should as well. 658284555Sarybchik */ 659284555Sarybchik EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 660227569Sphilip 661284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 662284555Sarybchik req.emr_cmd = MC_CMD_REBOOT; 663284555Sarybchik req.emr_in_buf = payload; 664284555Sarybchik req.emr_in_length = MC_CMD_REBOOT_IN_LEN; 665284555Sarybchik req.emr_out_buf = payload; 666284555Sarybchik req.emr_out_length = MC_CMD_REBOOT_OUT_LEN; 667227569Sphilip 668284555Sarybchik MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 669284555Sarybchik (after_assertion ? MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION : 0)); 670227569Sphilip 671284555Sarybchik efx_mcdi_execute_quiet(enp, &req); 672227569Sphilip 673284555Sarybchik if (req.emr_rc == EACCES) { 674284555Sarybchik /* Unprivileged functions cannot reboot the MC. */ 675284555Sarybchik goto out; 676284555Sarybchik } 677284555Sarybchik 678284555Sarybchik /* A successful reboot request returns EIO. */ 679284555Sarybchik if (req.emr_rc != 0 && req.emr_rc != EIO) { 680284555Sarybchik rc = req.emr_rc; 681227569Sphilip goto fail1; 682227569Sphilip } 683227569Sphilip 684284555Sarybchikout: 685284555Sarybchik return (0); 686284555Sarybchik 687284555Sarybchikfail1: 688284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 689284555Sarybchik 690284555Sarybchik return (rc); 691284555Sarybchik} 692284555Sarybchik 693284555Sarybchik __checkReturn int 694284555Sarybchikefx_mcdi_reboot( 695284555Sarybchik __in efx_nic_t *enp) 696284555Sarybchik{ 697284555Sarybchik return (efx_mcdi_do_reboot(enp, B_FALSE)); 698284555Sarybchik} 699284555Sarybchik 700284555Sarybchik __checkReturn int 701284555Sarybchikefx_mcdi_exit_assertion_handler( 702284555Sarybchik __in efx_nic_t *enp) 703284555Sarybchik{ 704284555Sarybchik return (efx_mcdi_do_reboot(enp, B_TRUE)); 705284555Sarybchik} 706284555Sarybchik 707284555Sarybchik __checkReturn int 708284555Sarybchikefx_mcdi_read_assertion( 709284555Sarybchik __in efx_nic_t *enp) 710284555Sarybchik{ 711284555Sarybchik efx_mcdi_req_t req; 712284555Sarybchik uint8_t payload[MAX(MC_CMD_GET_ASSERTS_IN_LEN, 713284555Sarybchik MC_CMD_GET_ASSERTS_OUT_LEN)]; 714284555Sarybchik const char *reason; 715284555Sarybchik unsigned int flags; 716284555Sarybchik unsigned int index; 717284555Sarybchik unsigned int ofst; 718284555Sarybchik int retry; 719284555Sarybchik int rc; 720284555Sarybchik 721227569Sphilip /* 722284555Sarybchik * Before we attempt to chat to the MC, we should verify that the MC 723284555Sarybchik * isn't in it's assertion handler, either due to a previous reboot, 724284555Sarybchik * or because we're reinitializing due to an eec_exception(). 725284555Sarybchik * 726284555Sarybchik * Use GET_ASSERTS to read any assertion state that may be present. 727284555Sarybchik * Retry this command twice. Once because a boot-time assertion failure 728284555Sarybchik * might cause the 1st MCDI request to fail. And once again because 729284555Sarybchik * we might race with efx_mcdi_exit_assertion_handler() running on 730284555Sarybchik * partner port(s) on the same NIC. 731227569Sphilip */ 732284555Sarybchik retry = 2; 733284555Sarybchik do { 734284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 735284555Sarybchik req.emr_cmd = MC_CMD_GET_ASSERTS; 736284555Sarybchik req.emr_in_buf = payload; 737284555Sarybchik req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN; 738284555Sarybchik req.emr_out_buf = payload; 739284555Sarybchik req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN; 740227569Sphilip 741284555Sarybchik MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1); 742284555Sarybchik efx_mcdi_execute_quiet(enp, &req); 743284555Sarybchik 744284555Sarybchik } while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0); 745284555Sarybchik 746284555Sarybchik if (req.emr_rc != 0) { 747284555Sarybchik if (req.emr_rc == EACCES) { 748284555Sarybchik /* Unprivileged functions cannot clear assertions. */ 749284555Sarybchik goto out; 750284555Sarybchik } 751284555Sarybchik rc = req.emr_rc; 752284555Sarybchik goto fail1; 753284555Sarybchik } 754284555Sarybchik 755284555Sarybchik if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) { 756284555Sarybchik rc = EMSGSIZE; 757284555Sarybchik goto fail2; 758284555Sarybchik } 759284555Sarybchik 760284555Sarybchik /* Print out any assertion state recorded */ 761284555Sarybchik flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS); 762284555Sarybchik if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) 763284555Sarybchik return (0); 764284555Sarybchik 765284555Sarybchik reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) 766284555Sarybchik ? "system-level assertion" 767284555Sarybchik : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) 768284555Sarybchik ? "thread-level assertion" 769284555Sarybchik : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED) 770284555Sarybchik ? "watchdog reset" 771284555Sarybchik : (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP) 772284555Sarybchik ? "illegal address trap" 773284555Sarybchik : "unknown assertion"; 774284555Sarybchik EFSYS_PROBE3(mcpu_assertion, 775284555Sarybchik const char *, reason, unsigned int, 776284555Sarybchik MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS), 777284555Sarybchik unsigned int, 778284555Sarybchik MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS)); 779284555Sarybchik 780284555Sarybchik /* Print out the registers (r1 ... r31) */ 781284555Sarybchik ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; 782284555Sarybchik for (index = 1; 783284555Sarybchik index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM; 784284555Sarybchik index++) { 785284555Sarybchik EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int, 786284555Sarybchik EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst), 787284555Sarybchik EFX_DWORD_0)); 788284555Sarybchik ofst += sizeof (efx_dword_t); 789284555Sarybchik } 790284555Sarybchik EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN); 791284555Sarybchik 792284555Sarybchikout: 793227569Sphilip return (0); 794227569Sphilip 795284555Sarybchikfail2: 796284555Sarybchik EFSYS_PROBE(fail2); 797227569Sphilipfail1: 798227569Sphilip EFSYS_PROBE1(fail1, int, rc); 799227569Sphilip 800227569Sphilip return (rc); 801227569Sphilip} 802227569Sphilip 803227569Sphilip 804284555Sarybchik/* 805284555Sarybchik * Internal routines for for specific MCDI requests. 806284555Sarybchik */ 807284555Sarybchik 808227569Sphilip __checkReturn int 809284555Sarybchikefx_mcdi_drv_attach( 810284555Sarybchik __in efx_nic_t *enp, 811284555Sarybchik __in boolean_t attach) 812227569Sphilip{ 813284555Sarybchik efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 814227569Sphilip efx_mcdi_req_t req; 815284555Sarybchik uint8_t payload[MAX(MC_CMD_DRV_ATTACH_IN_LEN, 816284555Sarybchik MC_CMD_DRV_ATTACH_EXT_OUT_LEN)]; 817284555Sarybchik uint32_t flags; 818227569Sphilip int rc; 819227569Sphilip 820284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 821284555Sarybchik req.emr_cmd = MC_CMD_DRV_ATTACH; 822284555Sarybchik req.emr_in_buf = payload; 823284555Sarybchik req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN; 824284555Sarybchik req.emr_out_buf = payload; 825284555Sarybchik req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN; 826284555Sarybchik 827227569Sphilip /* 828284555Sarybchik * Use DONT_CARE for the datapath firmware type to ensure that the 829284555Sarybchik * driver can attach to an unprivileged function. The datapath firmware 830284555Sarybchik * type to use is controlled by the 'sfboot' utility. 831227569Sphilip */ 832284555Sarybchik MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_NEW_STATE, attach ? 1 : 0); 833284555Sarybchik MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1); 834284555Sarybchik MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, MC_CMD_FW_DONT_CARE); 835227569Sphilip 836284555Sarybchik efx_mcdi_execute(enp, &req); 837284555Sarybchik 838284555Sarybchik if (req.emr_rc != 0) { 839284555Sarybchik rc = req.emr_rc; 840284555Sarybchik goto fail1; 841284555Sarybchik } 842284555Sarybchik 843284555Sarybchik if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) { 844284555Sarybchik rc = EMSGSIZE; 845284555Sarybchik goto fail2; 846284555Sarybchik } 847284555Sarybchik 848284555Sarybchik if (attach == B_FALSE) { 849284555Sarybchik flags = 0; 850284555Sarybchik } else if (enp->en_family == EFX_FAMILY_SIENA) { 851284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 852284555Sarybchik 853284555Sarybchik /* Create synthetic privileges for Siena functions */ 854284555Sarybchik flags = EFX_NIC_FUNC_LINKCTRL | EFX_NIC_FUNC_TRUSTED; 855284555Sarybchik if (emip->emi_port == 1) 856284555Sarybchik flags |= EFX_NIC_FUNC_PRIMARY; 857284555Sarybchik } else { 858284555Sarybchik EFX_STATIC_ASSERT(EFX_NIC_FUNC_PRIMARY == 859284555Sarybchik (1u << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY)); 860284555Sarybchik EFX_STATIC_ASSERT(EFX_NIC_FUNC_LINKCTRL == 861284555Sarybchik (1u << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL)); 862284555Sarybchik EFX_STATIC_ASSERT(EFX_NIC_FUNC_TRUSTED == 863284555Sarybchik (1u << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED)); 864284555Sarybchik 865284555Sarybchik /* Save function privilege flags (EF10 and later) */ 866284555Sarybchik if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_EXT_OUT_LEN) { 867284555Sarybchik rc = EMSGSIZE; 868284555Sarybchik goto fail3; 869284555Sarybchik } 870284555Sarybchik flags = MCDI_OUT_DWORD(req, DRV_ATTACH_EXT_OUT_FUNC_FLAGS); 871284555Sarybchik } 872284555Sarybchik encp->enc_func_flags = flags; 873284555Sarybchik 874284555Sarybchik return (0); 875284555Sarybchik 876284555Sarybchikfail3: 877284555Sarybchik EFSYS_PROBE(fail3); 878284555Sarybchikfail2: 879284555Sarybchik EFSYS_PROBE(fail2); 880284555Sarybchikfail1: 881284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 882284555Sarybchik 883284555Sarybchik return (rc); 884284555Sarybchik} 885284555Sarybchik 886284555Sarybchik __checkReturn int 887284555Sarybchikefx_mcdi_get_board_cfg( 888284555Sarybchik __in efx_nic_t *enp, 889284555Sarybchik __out_opt uint32_t *board_typep, 890284555Sarybchik __out_opt efx_dword_t *capabilitiesp, 891284555Sarybchik __out_ecount_opt(6) uint8_t mac_addrp[6]) 892284555Sarybchik{ 893284555Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 894284555Sarybchik efx_mcdi_req_t req; 895284555Sarybchik uint8_t payload[MAX(MC_CMD_GET_BOARD_CFG_IN_LEN, 896284555Sarybchik MC_CMD_GET_BOARD_CFG_OUT_LENMIN)]; 897284555Sarybchik int rc; 898284555Sarybchik 899284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 900284555Sarybchik req.emr_cmd = MC_CMD_GET_BOARD_CFG; 901227569Sphilip req.emr_in_buf = payload; 902284555Sarybchik req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN; 903284555Sarybchik req.emr_out_buf = payload; 904284555Sarybchik req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN; 905284555Sarybchik 906284555Sarybchik efx_mcdi_execute(enp, &req); 907284555Sarybchik 908284555Sarybchik if (req.emr_rc != 0) { 909284555Sarybchik rc = req.emr_rc; 910284555Sarybchik goto fail1; 911284555Sarybchik } 912284555Sarybchik 913284555Sarybchik if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) { 914284555Sarybchik rc = EMSGSIZE; 915284555Sarybchik goto fail2; 916284555Sarybchik } 917284555Sarybchik 918284555Sarybchik if (mac_addrp != NULL) { 919284555Sarybchik uint8_t *addrp; 920284555Sarybchik 921284555Sarybchik if (emip->emi_port == 1) { 922284555Sarybchik addrp = MCDI_OUT2(req, uint8_t, 923284555Sarybchik GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0); 924284555Sarybchik } else if (emip->emi_port == 2) { 925284555Sarybchik addrp = MCDI_OUT2(req, uint8_t, 926284555Sarybchik GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1); 927284555Sarybchik } else { 928284555Sarybchik rc = EINVAL; 929284555Sarybchik goto fail3; 930284555Sarybchik } 931284555Sarybchik 932284555Sarybchik EFX_MAC_ADDR_COPY(mac_addrp, addrp); 933284555Sarybchik } 934284555Sarybchik 935284555Sarybchik if (capabilitiesp != NULL) { 936284555Sarybchik if (emip->emi_port == 1) { 937284555Sarybchik *capabilitiesp = *MCDI_OUT2(req, efx_dword_t, 938284555Sarybchik GET_BOARD_CFG_OUT_CAPABILITIES_PORT0); 939284555Sarybchik } else if (emip->emi_port == 2) { 940284555Sarybchik *capabilitiesp = *MCDI_OUT2(req, efx_dword_t, 941284555Sarybchik GET_BOARD_CFG_OUT_CAPABILITIES_PORT1); 942284555Sarybchik } else { 943284555Sarybchik rc = EINVAL; 944284555Sarybchik goto fail4; 945284555Sarybchik } 946284555Sarybchik } 947284555Sarybchik 948284555Sarybchik if (board_typep != NULL) { 949284555Sarybchik *board_typep = MCDI_OUT_DWORD(req, 950284555Sarybchik GET_BOARD_CFG_OUT_BOARD_TYPE); 951284555Sarybchik } 952284555Sarybchik 953284555Sarybchik return (0); 954284555Sarybchik 955284555Sarybchikfail4: 956284555Sarybchik EFSYS_PROBE(fail4); 957284555Sarybchikfail3: 958284555Sarybchik EFSYS_PROBE(fail3); 959284555Sarybchikfail2: 960284555Sarybchik EFSYS_PROBE(fail2); 961284555Sarybchikfail1: 962284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 963284555Sarybchik 964284555Sarybchik return (rc); 965284555Sarybchik} 966284555Sarybchik 967284555Sarybchik __checkReturn int 968284555Sarybchikefx_mcdi_get_resource_limits( 969284555Sarybchik __in efx_nic_t *enp, 970284555Sarybchik __out_opt uint32_t *nevqp, 971284555Sarybchik __out_opt uint32_t *nrxqp, 972284555Sarybchik __out_opt uint32_t *ntxqp) 973284555Sarybchik{ 974284555Sarybchik efx_mcdi_req_t req; 975284555Sarybchik uint8_t payload[MAX(MC_CMD_GET_RESOURCE_LIMITS_IN_LEN, 976284555Sarybchik MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN)]; 977284555Sarybchik int rc; 978284555Sarybchik 979284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 980284555Sarybchik req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS; 981284555Sarybchik req.emr_in_buf = payload; 982284555Sarybchik req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN; 983284555Sarybchik req.emr_out_buf = payload; 984284555Sarybchik req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN; 985284555Sarybchik 986284555Sarybchik efx_mcdi_execute(enp, &req); 987284555Sarybchik 988284555Sarybchik if (req.emr_rc != 0) { 989284555Sarybchik rc = req.emr_rc; 990284555Sarybchik goto fail1; 991284555Sarybchik } 992284555Sarybchik 993284555Sarybchik if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) { 994284555Sarybchik rc = EMSGSIZE; 995284555Sarybchik goto fail2; 996284555Sarybchik } 997284555Sarybchik 998284555Sarybchik if (nevqp != NULL) 999284555Sarybchik *nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ); 1000284555Sarybchik if (nrxqp != NULL) 1001284555Sarybchik *nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ); 1002284555Sarybchik if (ntxqp != NULL) 1003284555Sarybchik *ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ); 1004284555Sarybchik 1005284555Sarybchik return (0); 1006284555Sarybchik 1007284555Sarybchikfail2: 1008284555Sarybchik EFSYS_PROBE(fail2); 1009284555Sarybchikfail1: 1010284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1011284555Sarybchik 1012284555Sarybchik return (rc); 1013284555Sarybchik} 1014284555Sarybchik 1015284555Sarybchik __checkReturn int 1016284555Sarybchikefx_mcdi_get_phy_cfg( 1017284555Sarybchik __in efx_nic_t *enp) 1018284555Sarybchik{ 1019284555Sarybchik efx_port_t *epp = &(enp->en_port); 1020284555Sarybchik efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 1021284555Sarybchik efx_mcdi_req_t req; 1022284555Sarybchik uint8_t payload[MAX(MC_CMD_GET_PHY_CFG_IN_LEN, 1023284555Sarybchik MC_CMD_GET_PHY_CFG_OUT_LEN)]; 1024284555Sarybchik int rc; 1025284555Sarybchik 1026284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1027284555Sarybchik req.emr_cmd = MC_CMD_GET_PHY_CFG; 1028284555Sarybchik req.emr_in_buf = payload; 1029284555Sarybchik req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN; 1030284555Sarybchik req.emr_out_buf = payload; 1031284555Sarybchik req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN; 1032284555Sarybchik 1033284555Sarybchik efx_mcdi_execute(enp, &req); 1034284555Sarybchik 1035284555Sarybchik if (req.emr_rc != 0) { 1036284555Sarybchik rc = req.emr_rc; 1037284555Sarybchik goto fail1; 1038284555Sarybchik } 1039284555Sarybchik 1040284555Sarybchik if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) { 1041284555Sarybchik rc = EMSGSIZE; 1042284555Sarybchik goto fail2; 1043284555Sarybchik } 1044284555Sarybchik 1045284555Sarybchik encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE); 1046284555Sarybchik#if EFSYS_OPT_NAMES 1047284555Sarybchik (void) strncpy(encp->enc_phy_name, 1048284555Sarybchik MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME), 1049284555Sarybchik MIN(sizeof (encp->enc_phy_name) - 1, 1050284555Sarybchik MC_CMD_GET_PHY_CFG_OUT_NAME_LEN)); 1051284555Sarybchik#endif /* EFSYS_OPT_NAMES */ 1052284555Sarybchik (void) memset(encp->enc_phy_revision, 0, 1053284555Sarybchik sizeof (encp->enc_phy_revision)); 1054284555Sarybchik memcpy(encp->enc_phy_revision, 1055284555Sarybchik MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION), 1056284555Sarybchik MIN(sizeof (encp->enc_phy_revision) - 1, 1057284555Sarybchik MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN)); 1058284555Sarybchik#if EFSYS_OPT_PHY_LED_CONTROL 1059284555Sarybchik encp->enc_led_mask = ((1 << EFX_PHY_LED_DEFAULT) | 1060284555Sarybchik (1 << EFX_PHY_LED_OFF) | 1061284555Sarybchik (1 << EFX_PHY_LED_ON)); 1062284555Sarybchik#endif /* EFSYS_OPT_PHY_LED_CONTROL */ 1063284555Sarybchik 1064284555Sarybchik#if EFSYS_OPT_PHY_PROPS 1065284555Sarybchik encp->enc_phy_nprops = 0; 1066284555Sarybchik#endif /* EFSYS_OPT_PHY_PROPS */ 1067284555Sarybchik 1068284555Sarybchik /* Get the media type of the fixed port, if recognised. */ 1069284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI); 1070284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4); 1071284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4); 1072284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP); 1073284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS); 1074284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T); 1075284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS); 1076284555Sarybchik epp->ep_fixed_port_type = 1077284555Sarybchik MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE); 1078284555Sarybchik if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES) 1079284555Sarybchik epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID; 1080284555Sarybchik 1081284555Sarybchik epp->ep_phy_cap_mask = 1082284555Sarybchik MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP); 1083284555Sarybchik#if EFSYS_OPT_PHY_FLAGS 1084284555Sarybchik encp->enc_phy_flags_mask = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_FLAGS); 1085284555Sarybchik#endif /* EFSYS_OPT_PHY_FLAGS */ 1086284555Sarybchik 1087284555Sarybchik encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT); 1088284555Sarybchik 1089284555Sarybchik /* Populate internal state */ 1090284555Sarybchik encp->enc_mcdi_mdio_channel = 1091284555Sarybchik (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL); 1092284555Sarybchik 1093284555Sarybchik#if EFSYS_OPT_PHY_STATS 1094284555Sarybchik encp->enc_mcdi_phy_stat_mask = 1095284555Sarybchik MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_STATS_MASK); 1096284555Sarybchik#endif /* EFSYS_OPT_PHY_STATS */ 1097284555Sarybchik 1098284555Sarybchik#if EFSYS_OPT_BIST 1099284555Sarybchik encp->enc_bist_mask = 0; 1100284555Sarybchik if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS, 1101284555Sarybchik GET_PHY_CFG_OUT_BIST_CABLE_SHORT)) 1102284555Sarybchik encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT); 1103284555Sarybchik if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS, 1104284555Sarybchik GET_PHY_CFG_OUT_BIST_CABLE_LONG)) 1105284555Sarybchik encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG); 1106284555Sarybchik if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS, 1107284555Sarybchik GET_PHY_CFG_OUT_BIST)) 1108284555Sarybchik encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL); 1109284555Sarybchik#endif /* EFSYS_OPT_BIST */ 1110284555Sarybchik 1111284555Sarybchik return (0); 1112284555Sarybchik 1113284555Sarybchikfail2: 1114284555Sarybchik EFSYS_PROBE(fail2); 1115284555Sarybchikfail1: 1116284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1117284555Sarybchik 1118284555Sarybchik return (rc); 1119284555Sarybchik} 1120284555Sarybchik 1121284555Sarybchik 1122284555Sarybchik __checkReturn int 1123284555Sarybchikefx_mcdi_firmware_update_supported( 1124284555Sarybchik __in efx_nic_t *enp, 1125284555Sarybchik __out boolean_t *supportedp) 1126284555Sarybchik{ 1127284555Sarybchik efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop; 1128284555Sarybchik int rc; 1129284555Sarybchik 1130284555Sarybchik if (emcop != NULL && emcop->emco_fw_update_supported != NULL) { 1131284555Sarybchik if ((rc = emcop->emco_fw_update_supported(enp, supportedp)) 1132284555Sarybchik != 0) 1133284555Sarybchik goto fail1; 1134284555Sarybchik } else { 1135284555Sarybchik /* Earlier devices always supported updates */ 1136284555Sarybchik *supportedp = B_TRUE; 1137284555Sarybchik } 1138284555Sarybchik 1139284555Sarybchik return (0); 1140284555Sarybchik 1141284555Sarybchikfail1: 1142284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1143284555Sarybchik 1144284555Sarybchik return (rc); 1145284555Sarybchik} 1146284555Sarybchik 1147284555Sarybchik __checkReturn int 1148284555Sarybchikefx_mcdi_macaddr_change_supported( 1149284555Sarybchik __in efx_nic_t *enp, 1150284555Sarybchik __out boolean_t *supportedp) 1151284555Sarybchik{ 1152284555Sarybchik efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop; 1153284555Sarybchik int rc; 1154284555Sarybchik 1155284555Sarybchik if (emcop != NULL && emcop->emco_macaddr_change_supported != NULL) { 1156284555Sarybchik if ((rc = emcop->emco_macaddr_change_supported(enp, supportedp)) 1157284555Sarybchik != 0) 1158284555Sarybchik goto fail1; 1159284555Sarybchik } else { 1160284555Sarybchik /* Earlier devices always supported MAC changes */ 1161284555Sarybchik *supportedp = B_TRUE; 1162284555Sarybchik } 1163284555Sarybchik 1164284555Sarybchik return (0); 1165284555Sarybchik 1166284555Sarybchikfail1: 1167284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1168284555Sarybchik 1169284555Sarybchik return (rc); 1170284555Sarybchik} 1171284555Sarybchik 1172284555Sarybchik#if EFSYS_OPT_BIST 1173284555Sarybchik 1174284555Sarybchik#if EFSYS_OPT_HUNTINGTON 1175284555Sarybchik/* 1176284555Sarybchik * Enter bist offline mode. This is a fw mode which puts the NIC into a state 1177284555Sarybchik * where memory BIST tests can be run and not much else can interfere or happen. 1178284555Sarybchik * A reboot is required to exit this mode. 1179284555Sarybchik */ 1180284555Sarybchik __checkReturn int 1181284555Sarybchikefx_mcdi_bist_enable_offline( 1182284555Sarybchik __in efx_nic_t *enp) 1183284555Sarybchik{ 1184284555Sarybchik efx_mcdi_req_t req; 1185284555Sarybchik int rc; 1186284555Sarybchik 1187284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0); 1188284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0); 1189284555Sarybchik 1190284555Sarybchik req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST; 1191284555Sarybchik req.emr_in_buf = NULL; 1192284555Sarybchik req.emr_in_length = 0; 1193227569Sphilip req.emr_out_buf = NULL; 1194227569Sphilip req.emr_out_length = 0; 1195227569Sphilip 1196284555Sarybchik efx_mcdi_execute(enp, &req); 1197227569Sphilip 1198284555Sarybchik if (req.emr_rc != 0) { 1199284555Sarybchik rc = req.emr_rc; 1200284555Sarybchik goto fail1; 1201284555Sarybchik } 1202284555Sarybchik 1203284555Sarybchik return (0); 1204284555Sarybchik 1205284555Sarybchikfail1: 1206284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1207284555Sarybchik 1208284555Sarybchik return (rc); 1209284555Sarybchik} 1210284555Sarybchik#endif /* EFSYS_OPT_HUNTINGTON */ 1211284555Sarybchik 1212284555Sarybchik __checkReturn int 1213284555Sarybchikefx_mcdi_bist_start( 1214284555Sarybchik __in efx_nic_t *enp, 1215284555Sarybchik __in efx_bist_type_t type) 1216284555Sarybchik{ 1217284555Sarybchik efx_mcdi_req_t req; 1218284555Sarybchik uint8_t payload[MAX(MC_CMD_START_BIST_IN_LEN, 1219284555Sarybchik MC_CMD_START_BIST_OUT_LEN)]; 1220284555Sarybchik int rc; 1221284555Sarybchik 1222284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1223284555Sarybchik req.emr_cmd = MC_CMD_START_BIST; 1224284555Sarybchik req.emr_in_buf = payload; 1225284555Sarybchik req.emr_in_length = MC_CMD_START_BIST_IN_LEN; 1226284555Sarybchik req.emr_out_buf = payload; 1227284555Sarybchik req.emr_out_length = MC_CMD_START_BIST_OUT_LEN; 1228284555Sarybchik 1229284555Sarybchik switch (type) { 1230284555Sarybchik case EFX_BIST_TYPE_PHY_NORMAL: 1231284555Sarybchik MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST); 1232284555Sarybchik break; 1233284555Sarybchik case EFX_BIST_TYPE_PHY_CABLE_SHORT: 1234284555Sarybchik MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, 1235284555Sarybchik MC_CMD_PHY_BIST_CABLE_SHORT); 1236284555Sarybchik break; 1237284555Sarybchik case EFX_BIST_TYPE_PHY_CABLE_LONG: 1238284555Sarybchik MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, 1239284555Sarybchik MC_CMD_PHY_BIST_CABLE_LONG); 1240284555Sarybchik break; 1241284555Sarybchik case EFX_BIST_TYPE_MC_MEM: 1242284555Sarybchik MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, 1243284555Sarybchik MC_CMD_MC_MEM_BIST); 1244284555Sarybchik break; 1245284555Sarybchik case EFX_BIST_TYPE_SAT_MEM: 1246284555Sarybchik MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, 1247284555Sarybchik MC_CMD_PORT_MEM_BIST); 1248284555Sarybchik break; 1249284555Sarybchik case EFX_BIST_TYPE_REG: 1250284555Sarybchik MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, 1251284555Sarybchik MC_CMD_REG_BIST); 1252284555Sarybchik break; 1253284555Sarybchik default: 1254284555Sarybchik EFSYS_ASSERT(0); 1255284555Sarybchik } 1256284555Sarybchik 1257227569Sphilip efx_mcdi_execute(enp, &req); 1258227569Sphilip 1259284555Sarybchik if (req.emr_rc != 0) { 1260284555Sarybchik rc = req.emr_rc; 1261227569Sphilip goto fail1; 1262227569Sphilip } 1263227569Sphilip 1264227569Sphilip return (0); 1265227569Sphilip 1266227569Sphilipfail1: 1267227569Sphilip EFSYS_PROBE1(fail1, int, rc); 1268227569Sphilip 1269227569Sphilip return (rc); 1270227569Sphilip} 1271227569Sphilip 1272284555Sarybchik#endif /* EFSYS_OPT_BIST */ 1273284555Sarybchik 1274284555Sarybchik 1275284555Sarybchik/* Enable logging of some events (e.g. link state changes) */ 1276284555Sarybchik __checkReturn int 1277284555Sarybchikefx_mcdi_log_ctrl( 1278227569Sphilip __in efx_nic_t *enp) 1279227569Sphilip{ 1280284555Sarybchik efx_mcdi_req_t req; 1281284555Sarybchik uint8_t payload[MAX(MC_CMD_LOG_CTRL_IN_LEN, 1282284555Sarybchik MC_CMD_LOG_CTRL_OUT_LEN)]; 1283284555Sarybchik int rc; 1284227569Sphilip 1285284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1286284555Sarybchik req.emr_cmd = MC_CMD_LOG_CTRL; 1287284555Sarybchik req.emr_in_buf = payload; 1288284555Sarybchik req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN; 1289284555Sarybchik req.emr_out_buf = payload; 1290284555Sarybchik req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN; 1291227569Sphilip 1292284555Sarybchik MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST, 1293284555Sarybchik MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ); 1294284555Sarybchik MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0); 1295284555Sarybchik 1296284555Sarybchik efx_mcdi_execute(enp, &req); 1297284555Sarybchik 1298284555Sarybchik if (req.emr_rc != 0) { 1299284555Sarybchik rc = req.emr_rc; 1300284555Sarybchik goto fail1; 1301284555Sarybchik } 1302284555Sarybchik 1303284555Sarybchik return (0); 1304284555Sarybchik 1305284555Sarybchikfail1: 1306284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1307284555Sarybchik 1308284555Sarybchik return (rc); 1309284555Sarybchik} 1310284555Sarybchik 1311284555Sarybchik 1312284555Sarybchik#if EFSYS_OPT_MAC_STATS 1313284555Sarybchik 1314284555Sarybchiktypedef enum efx_stats_action_e 1315284555Sarybchik{ 1316284555Sarybchik EFX_STATS_CLEAR, 1317284555Sarybchik EFX_STATS_UPLOAD, 1318284555Sarybchik EFX_STATS_ENABLE_NOEVENTS, 1319284555Sarybchik EFX_STATS_ENABLE_EVENTS, 1320284555Sarybchik EFX_STATS_DISABLE, 1321284555Sarybchik} efx_stats_action_t; 1322284555Sarybchik 1323284555Sarybchikstatic __checkReturn int 1324284555Sarybchikefx_mcdi_mac_stats( 1325284555Sarybchik __in efx_nic_t *enp, 1326284555Sarybchik __in_opt efsys_mem_t *esmp, 1327284555Sarybchik __in efx_stats_action_t action) 1328284555Sarybchik{ 1329284555Sarybchik efx_mcdi_req_t req; 1330284555Sarybchik uint8_t payload[MAX(MC_CMD_MAC_STATS_IN_LEN, 1331284555Sarybchik MC_CMD_MAC_STATS_OUT_DMA_LEN)]; 1332284555Sarybchik int clear = (action == EFX_STATS_CLEAR); 1333284555Sarybchik int upload = (action == EFX_STATS_UPLOAD); 1334284555Sarybchik int enable = (action == EFX_STATS_ENABLE_NOEVENTS); 1335284555Sarybchik int events = (action == EFX_STATS_ENABLE_EVENTS); 1336284555Sarybchik int disable = (action == EFX_STATS_DISABLE); 1337284555Sarybchik int rc; 1338284555Sarybchik 1339284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1340284555Sarybchik req.emr_cmd = MC_CMD_MAC_STATS; 1341284555Sarybchik req.emr_in_buf = payload; 1342284555Sarybchik req.emr_in_length = MC_CMD_MAC_STATS_IN_LEN; 1343284555Sarybchik req.emr_out_buf = payload; 1344284555Sarybchik req.emr_out_length = MC_CMD_MAC_STATS_OUT_DMA_LEN; 1345284555Sarybchik 1346284555Sarybchik MCDI_IN_POPULATE_DWORD_6(req, MAC_STATS_IN_CMD, 1347284555Sarybchik MAC_STATS_IN_DMA, upload, 1348284555Sarybchik MAC_STATS_IN_CLEAR, clear, 1349284555Sarybchik MAC_STATS_IN_PERIODIC_CHANGE, enable | events | disable, 1350284555Sarybchik MAC_STATS_IN_PERIODIC_ENABLE, enable | events, 1351284555Sarybchik MAC_STATS_IN_PERIODIC_NOEVENT, !events, 1352284555Sarybchik MAC_STATS_IN_PERIOD_MS, (enable | events) ? 1000: 0); 1353284555Sarybchik 1354284555Sarybchik if (esmp != NULL) { 1355284555Sarybchik int bytes = MC_CMD_MAC_NSTATS * sizeof (uint64_t); 1356284555Sarybchik 1357284555Sarybchik EFX_STATIC_ASSERT(MC_CMD_MAC_NSTATS * sizeof (uint64_t) <= 1358284555Sarybchik EFX_MAC_STATS_SIZE); 1359284555Sarybchik 1360284555Sarybchik MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_LO, 1361284555Sarybchik EFSYS_MEM_ADDR(esmp) & 0xffffffff); 1362284555Sarybchik MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_HI, 1363284555Sarybchik EFSYS_MEM_ADDR(esmp) >> 32); 1364284555Sarybchik MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_LEN, bytes); 1365284555Sarybchik } else { 1366284555Sarybchik EFSYS_ASSERT(!upload && !enable && !events); 1367284555Sarybchik } 1368284555Sarybchik 1369227569Sphilip /* 1370284555Sarybchik * NOTE: Do not use EVB_PORT_ID_ASSIGNED when disabling periodic stats, 1371284555Sarybchik * as this may fail (and leave periodic DMA enabled) if the 1372284555Sarybchik * vadapter has already been deleted. 1373227569Sphilip */ 1374284555Sarybchik MCDI_IN_SET_DWORD(req, MAC_STATS_IN_PORT_ID, 1375284555Sarybchik (disable ? EVB_PORT_ID_NULL : enp->en_vport_id)); 1376227569Sphilip 1377284555Sarybchik efx_mcdi_execute(enp, &req); 1378227569Sphilip 1379284555Sarybchik if (req.emr_rc != 0) { 1380284555Sarybchik /* EF10: Expect ENOENT if no DMA queues are initialised */ 1381284555Sarybchik if ((req.emr_rc != ENOENT) || 1382284555Sarybchik (enp->en_rx_qcount + enp->en_tx_qcount != 0)) { 1383284555Sarybchik rc = req.emr_rc; 1384284555Sarybchik goto fail1; 1385284555Sarybchik } 1386227569Sphilip } 1387227569Sphilip 1388284555Sarybchik return (0); 1389284555Sarybchik 1390284555Sarybchikfail1: 1391284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1392284555Sarybchik 1393284555Sarybchik return (rc); 1394227569Sphilip} 1395227569Sphilip 1396284555Sarybchik __checkReturn int 1397284555Sarybchikefx_mcdi_mac_stats_clear( 1398227569Sphilip __in efx_nic_t *enp) 1399227569Sphilip{ 1400284555Sarybchik int rc; 1401227569Sphilip 1402284555Sarybchik if ((rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_CLEAR)) != 0) 1403284555Sarybchik goto fail1; 1404227569Sphilip 1405284555Sarybchik return (0); 1406227569Sphilip 1407284555Sarybchikfail1: 1408284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1409284555Sarybchik 1410284555Sarybchik return (rc); 1411227569Sphilip} 1412227569Sphilip 1413284555Sarybchik __checkReturn int 1414284555Sarybchikefx_mcdi_mac_stats_upload( 1415284555Sarybchik __in efx_nic_t *enp, 1416284555Sarybchik __in efsys_mem_t *esmp) 1417284555Sarybchik{ 1418284555Sarybchik int rc; 1419284555Sarybchik 1420284555Sarybchik /* 1421284555Sarybchik * The MC DMAs aggregate statistics for our convenience, so we can 1422284555Sarybchik * avoid having to pull the statistics buffer into the cache to 1423284555Sarybchik * maintain cumulative statistics. 1424284555Sarybchik */ 1425284555Sarybchik if ((rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_UPLOAD)) != 0) 1426284555Sarybchik goto fail1; 1427284555Sarybchik 1428284555Sarybchik return (0); 1429284555Sarybchik 1430284555Sarybchikfail1: 1431284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1432284555Sarybchik 1433284555Sarybchik return (rc); 1434284555Sarybchik} 1435284555Sarybchik 1436284555Sarybchik __checkReturn int 1437284555Sarybchikefx_mcdi_mac_stats_periodic( 1438284555Sarybchik __in efx_nic_t *enp, 1439284555Sarybchik __in efsys_mem_t *esmp, 1440284555Sarybchik __in uint16_t period, 1441284555Sarybchik __in boolean_t events) 1442284555Sarybchik{ 1443284555Sarybchik int rc; 1444284555Sarybchik 1445284555Sarybchik /* 1446284555Sarybchik * The MC DMAs aggregate statistics for our convenience, so we can 1447284555Sarybchik * avoid having to pull the statistics buffer into the cache to 1448284555Sarybchik * maintain cumulative statistics. 1449284555Sarybchik * Huntington uses a fixed 1sec period, so use that on Siena too. 1450284555Sarybchik */ 1451284555Sarybchik if (period == 0) 1452284555Sarybchik rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_DISABLE); 1453284555Sarybchik else if (events) 1454284555Sarybchik rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_EVENTS); 1455284555Sarybchik else 1456284555Sarybchik rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_NOEVENTS); 1457284555Sarybchik 1458284555Sarybchik if (rc != 0) 1459284555Sarybchik goto fail1; 1460284555Sarybchik 1461284555Sarybchik return (0); 1462284555Sarybchik 1463284555Sarybchikfail1: 1464284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1465284555Sarybchik 1466284555Sarybchik return (rc); 1467284555Sarybchik} 1468284555Sarybchik 1469284555Sarybchik#endif /* EFSYS_OPT_MAC_STATS */ 1470284555Sarybchik 1471284555Sarybchik#if EFSYS_OPT_HUNTINGTON 1472284555Sarybchik 1473284555Sarybchik/* 1474284555Sarybchik * This function returns the pf and vf number of a function. If it is a pf the 1475284555Sarybchik * vf number is 0xffff. The vf number is the index of the vf on that 1476284555Sarybchik * function. So if you have 3 vfs on pf 0 the 3 vfs will return (pf=0,vf=0), 1477284555Sarybchik * (pf=0,vf=1), (pf=0,vf=2) aand the pf will return (pf=0, vf=0xffff). 1478284555Sarybchik */ 1479284555Sarybchik __checkReturn int 1480284555Sarybchikefx_mcdi_get_function_info( 1481284555Sarybchik __in efx_nic_t *enp, 1482284555Sarybchik __out uint32_t *pfp, 1483284555Sarybchik __out_opt uint32_t *vfp) 1484284555Sarybchik{ 1485284555Sarybchik efx_mcdi_req_t req; 1486284555Sarybchik uint8_t payload[MAX(MC_CMD_GET_FUNCTION_INFO_IN_LEN, 1487284555Sarybchik MC_CMD_GET_FUNCTION_INFO_OUT_LEN)]; 1488284555Sarybchik int rc; 1489284555Sarybchik 1490284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1491284555Sarybchik req.emr_cmd = MC_CMD_GET_FUNCTION_INFO; 1492284555Sarybchik req.emr_in_buf = payload; 1493284555Sarybchik req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN; 1494284555Sarybchik req.emr_out_buf = payload; 1495284555Sarybchik req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN; 1496284555Sarybchik 1497284555Sarybchik efx_mcdi_execute(enp, &req); 1498284555Sarybchik 1499284555Sarybchik if (req.emr_rc != 0) { 1500284555Sarybchik rc = req.emr_rc; 1501284555Sarybchik goto fail1; 1502284555Sarybchik } 1503284555Sarybchik 1504284555Sarybchik if (req.emr_out_length_used < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) { 1505284555Sarybchik rc = EMSGSIZE; 1506284555Sarybchik goto fail2; 1507284555Sarybchik } 1508284555Sarybchik 1509284555Sarybchik *pfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_PF); 1510284555Sarybchik if (vfp != NULL) 1511284555Sarybchik *vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF); 1512284555Sarybchik 1513284555Sarybchik return (0); 1514284555Sarybchik 1515284555Sarybchikfail2: 1516284555Sarybchik EFSYS_PROBE(fail2); 1517284555Sarybchikfail1: 1518284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1519284555Sarybchik 1520284555Sarybchik return (rc); 1521284555Sarybchik} 1522284555Sarybchik 1523284555Sarybchik __checkReturn int 1524284555Sarybchikefx_mcdi_privilege_mask( 1525284555Sarybchik __in efx_nic_t *enp, 1526284555Sarybchik __in uint32_t pf, 1527284555Sarybchik __in uint32_t vf, 1528284555Sarybchik __out uint32_t *maskp) 1529284555Sarybchik{ 1530284555Sarybchik efx_mcdi_req_t req; 1531284555Sarybchik uint8_t payload[MAX(MC_CMD_PRIVILEGE_MASK_IN_LEN, 1532284555Sarybchik MC_CMD_PRIVILEGE_MASK_OUT_LEN)]; 1533284555Sarybchik int rc; 1534284555Sarybchik 1535284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1536284555Sarybchik req.emr_cmd = MC_CMD_PRIVILEGE_MASK; 1537284555Sarybchik req.emr_in_buf = payload; 1538284555Sarybchik req.emr_in_length = MC_CMD_PRIVILEGE_MASK_IN_LEN; 1539284555Sarybchik req.emr_out_buf = payload; 1540284555Sarybchik req.emr_out_length = MC_CMD_PRIVILEGE_MASK_OUT_LEN; 1541284555Sarybchik 1542284555Sarybchik MCDI_IN_POPULATE_DWORD_2(req, PRIVILEGE_MASK_IN_FUNCTION, 1543284555Sarybchik PRIVILEGE_MASK_IN_FUNCTION_PF, pf, 1544284555Sarybchik PRIVILEGE_MASK_IN_FUNCTION_VF, vf); 1545284555Sarybchik 1546284555Sarybchik efx_mcdi_execute(enp, &req); 1547284555Sarybchik 1548284555Sarybchik if (req.emr_rc != 0) { 1549284555Sarybchik rc = req.emr_rc; 1550284555Sarybchik goto fail1; 1551284555Sarybchik } 1552284555Sarybchik 1553284555Sarybchik if (req.emr_out_length_used < MC_CMD_PRIVILEGE_MASK_OUT_LEN) { 1554284555Sarybchik rc = EMSGSIZE; 1555284555Sarybchik goto fail2; 1556284555Sarybchik } 1557284555Sarybchik 1558284555Sarybchik *maskp = MCDI_OUT_DWORD(req, PRIVILEGE_MASK_OUT_OLD_MASK); 1559284555Sarybchik 1560284555Sarybchik return (0); 1561284555Sarybchik 1562284555Sarybchikfail2: 1563284555Sarybchik EFSYS_PROBE(fail2); 1564284555Sarybchikfail1: 1565284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1566284555Sarybchik 1567284555Sarybchik return (rc); 1568284555Sarybchik} 1569284555Sarybchik 1570284555Sarybchik#endif /* EFSYS_OPT_HUNTINGTON */ 1571284555Sarybchik 1572284555Sarybchik __checkReturn int 1573284555Sarybchikefx_mcdi_set_workaround( 1574284555Sarybchik __in efx_nic_t *enp, 1575284555Sarybchik __in uint32_t type, 1576284555Sarybchik __in boolean_t enabled, 1577284555Sarybchik __out_opt uint32_t *flagsp) 1578284555Sarybchik{ 1579284555Sarybchik efx_mcdi_req_t req; 1580284555Sarybchik uint8_t payload[MAX(MC_CMD_WORKAROUND_IN_LEN, 1581284555Sarybchik MC_CMD_WORKAROUND_EXT_OUT_LEN)]; 1582284555Sarybchik int rc; 1583284555Sarybchik 1584284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1585284555Sarybchik req.emr_cmd = MC_CMD_WORKAROUND; 1586284555Sarybchik req.emr_in_buf = payload; 1587284555Sarybchik req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN; 1588284555Sarybchik req.emr_out_buf = payload; 1589284555Sarybchik req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN; 1590284555Sarybchik 1591284555Sarybchik MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type); 1592284555Sarybchik MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0); 1593284555Sarybchik 1594284555Sarybchik efx_mcdi_execute_quiet(enp, &req); 1595284555Sarybchik 1596284555Sarybchik if (req.emr_rc != 0) { 1597284555Sarybchik rc = req.emr_rc; 1598284555Sarybchik goto fail1; 1599284555Sarybchik } 1600284555Sarybchik 1601284555Sarybchik if (flagsp != NULL) { 1602284555Sarybchik if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN) 1603284555Sarybchik *flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS); 1604284555Sarybchik else 1605284555Sarybchik *flagsp = 0; 1606284555Sarybchik } 1607284555Sarybchik 1608284555Sarybchik return (0); 1609284555Sarybchik 1610284555Sarybchikfail1: 1611284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1612284555Sarybchik 1613284555Sarybchik return (rc); 1614284555Sarybchik} 1615284555Sarybchik 1616284555Sarybchik 1617284555Sarybchik __checkReturn int 1618284555Sarybchikefx_mcdi_get_workarounds( 1619284555Sarybchik __in efx_nic_t *enp, 1620284555Sarybchik __out_opt uint32_t *implementedp, 1621284555Sarybchik __out_opt uint32_t *enabledp) 1622284555Sarybchik{ 1623284555Sarybchik efx_mcdi_req_t req; 1624284555Sarybchik uint8_t payload[MC_CMD_GET_WORKAROUNDS_OUT_LEN]; 1625284555Sarybchik int rc; 1626284555Sarybchik 1627284555Sarybchik (void) memset(payload, 0, sizeof (payload)); 1628284555Sarybchik req.emr_cmd = MC_CMD_GET_WORKAROUNDS; 1629284555Sarybchik req.emr_in_buf = NULL; 1630284555Sarybchik req.emr_in_length = 0; 1631284555Sarybchik req.emr_out_buf = payload; 1632284555Sarybchik req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN; 1633284555Sarybchik 1634284555Sarybchik efx_mcdi_execute(enp, &req); 1635284555Sarybchik 1636284555Sarybchik if (req.emr_rc != 0) { 1637284555Sarybchik rc = req.emr_rc; 1638284555Sarybchik goto fail1; 1639284555Sarybchik } 1640284555Sarybchik 1641284555Sarybchik if (implementedp != NULL) { 1642284555Sarybchik *implementedp = 1643284555Sarybchik MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED); 1644284555Sarybchik } 1645284555Sarybchik 1646284555Sarybchik if (enabledp != NULL) { 1647284555Sarybchik *enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED); 1648284555Sarybchik } 1649284555Sarybchik 1650284555Sarybchik return (0); 1651284555Sarybchik 1652284555Sarybchikfail1: 1653284555Sarybchik EFSYS_PROBE1(fail1, int, rc); 1654284555Sarybchik 1655284555Sarybchik return (rc); 1656284555Sarybchik} 1657284555Sarybchik 1658284555Sarybchik 1659227569Sphilip#endif /* EFSYS_OPT_MCDI */ 1660