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