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