efx_mcdi.c revision 227569
1134459Siedowse/*- 2134459Siedowse * Copyright 2008-2009 Solarflare Communications Inc. All rights reserved. 3134459Siedowse * 4134459Siedowse * Redistribution and use in source and binary forms, with or without 5134459Siedowse * modification, are permitted provided that the following conditions 6134459Siedowse * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include "efsys.h" 27#include "efx.h" 28#include "efx_types.h" 29#include "efx_regs.h" 30#include "efx_regs_mcdi.h" 31#include "efx_impl.h" 32 33#if EFSYS_OPT_MCDI 34 35/* Shared memory layout */ 36 37#define MCDI_P1_DBL_OFST 0x0 38#define MCDI_P2_DBL_OFST 0x1 39#define MCDI_P1_PDU_OFST 0x2 40#define MCDI_P2_PDU_OFST 0x42 41#define MCDI_P1_REBOOT_OFST 0x1fe 42#define MCDI_P2_REBOOT_OFST 0x1ff 43 44/* A reboot/assertion causes the MCDI status word to be set after the 45 * command word is set or a REBOOT event is sent. If we notice a reboot 46 * via these mechanisms then wait 10ms for the status word to be set. 47 */ 48#define MCDI_STATUS_SLEEP_US 10000 49 50 void 51efx_mcdi_request_start( 52 __in efx_nic_t *enp, 53 __in efx_mcdi_req_t *emrp, 54 __in boolean_t ev_cpl) 55{ 56 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 57 efx_dword_t dword; 58 unsigned int seq; 59 unsigned int xflags; 60 unsigned int pdur; 61 unsigned int dbr; 62 unsigned int pos; 63 int state; 64 65 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 66 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 67 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 68 69 switch (emip->emi_port) { 70 case 1: 71 pdur = MCDI_P1_PDU_OFST; 72 dbr = MCDI_P1_DBL_OFST; 73 break; 74 case 2: 75 pdur = MCDI_P2_PDU_OFST; 76 dbr = MCDI_P2_DBL_OFST; 77 break; 78 default: 79 EFSYS_ASSERT(0); 80 pdur = dbr = 0; 81 }; 82 83 /* 84 * efx_mcdi_request_start() is naturally serialised against both 85 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(), 86 * by virtue of there only being one oustanding MCDI request. 87 * Unfortunately, upper layers may also call efx_mcdi_request_abort() 88 * at any time, to timeout a pending mcdi request, That request may 89 * then subsequently complete, meaning efx_mcdi_ev_cpl() or 90 * efx_mcdi_ev_death() may end up running in parallel with 91 * efx_mcdi_request_start(). This race is handled by ensuring that 92 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the 93 * en_eslp lock. 94 */ 95 EFSYS_LOCK(enp->en_eslp, state); 96 EFSYS_ASSERT(emip->emi_pending_req == NULL); 97 emip->emi_pending_req = emrp; 98 emip->emi_ev_cpl = ev_cpl; 99 emip->emi_poll_cnt = 0; 100 seq = emip->emi_seq++ & 0xf; 101 EFSYS_UNLOCK(enp->en_eslp, state); 102 103 xflags = 0; 104 if (ev_cpl) 105 xflags |= MCDI_HEADER_XFLAGS_EVREQ; 106 107 /* Construct the header in shared memory */ 108 EFX_POPULATE_DWORD_6(dword, 109 MCDI_HEADER_CODE, emrp->emr_cmd, 110 MCDI_HEADER_RESYNC, 1, 111 MCDI_HEADER_DATALEN, emrp->emr_in_length, 112 MCDI_HEADER_SEQ, seq, 113 MCDI_HEADER_RESPONSE, 0, 114 MCDI_HEADER_XFLAGS, xflags); 115 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE); 116 117 for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) { 118 memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos), 119 MIN(sizeof (dword), emrp->emr_in_length - pos)); 120 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, 121 pdur + 1 + (pos >> 2), &dword, B_FALSE); 122 } 123 124 /* Ring the doorbell */ 125 EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11); 126 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE); 127} 128 129static void 130efx_mcdi_request_copyout( 131 __in efx_nic_t *enp, 132 __in efx_mcdi_req_t *emrp) 133{ 134 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 135 unsigned int pos; 136 unsigned int pdur; 137 efx_dword_t data; 138 139 pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST; 140 141 /* Copy payload out if caller supplied buffer */ 142 if (emrp->emr_out_buf != NULL) { 143 size_t bytes = MIN(emrp->emr_out_length_used, 144 emrp->emr_out_length); 145 for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) { 146 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, 147 pdur + 1 + (pos >> 2), &data, B_FALSE); 148 memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data, 149 MIN(sizeof (data), bytes - pos)); 150 } 151 } 152} 153 154static int 155efx_mcdi_request_errcode( 156 __in unsigned int err) 157{ 158 159 switch (err) { 160 case MC_CMD_ERR_ENOENT: 161 return (ENOENT); 162 case MC_CMD_ERR_EINTR: 163 return (EINTR); 164 case MC_CMD_ERR_EACCES: 165 return (EACCES); 166 case MC_CMD_ERR_EBUSY: 167 return (EBUSY); 168 case MC_CMD_ERR_EINVAL: 169 return (EINVAL); 170 case MC_CMD_ERR_EDEADLK: 171 return (EDEADLK); 172 case MC_CMD_ERR_ENOSYS: 173 return (ENOTSUP); 174 case MC_CMD_ERR_ETIME: 175 return (ETIMEDOUT); 176#ifdef WITH_MCDI_V2 177 case MC_CMD_ERR_EAGAIN: 178 return (EAGAIN); 179 case MC_CMD_ERR_ENOSPC: 180 return (ENOSPC); 181#endif 182 default: 183 EFSYS_PROBE1(mc_pcol_error, int, err); 184 return (EIO); 185 } 186} 187 188static void 189efx_mcdi_raise_exception( 190 __in efx_nic_t *enp, 191 __in_opt efx_mcdi_req_t *emrp, 192 __in int rc) 193{ 194 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 195 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 196 efx_mcdi_exception_t exception; 197 198 /* Reboot or Assertion failure only */ 199 EFSYS_ASSERT(rc == EIO || rc == EINTR); 200 201 /* 202 * If MC_CMD_REBOOT causes a reboot (dependent on parameters), 203 * then the EIO is not worthy of an exception. 204 */ 205 if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO) 206 return; 207 208 exception = (rc == EIO) 209 ? EFX_MCDI_EXCEPTION_MC_REBOOT 210 : EFX_MCDI_EXCEPTION_MC_BADASSERT; 211 212 emtp->emt_exception(emtp->emt_context, exception); 213} 214 215static int 216efx_mcdi_poll_reboot( 217 __in efx_nic_t *enp) 218{ 219 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 220 unsigned int rebootr; 221 efx_dword_t dword; 222 uint32_t value; 223 224 EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2); 225 rebootr = ((emip->emi_port == 1) 226 ? MCDI_P1_REBOOT_OFST 227 : MCDI_P2_REBOOT_OFST); 228 229 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE); 230 value = EFX_DWORD_FIELD(dword, EFX_DWORD_0); 231 232 if (value == 0) 233 return (0); 234 235 EFX_ZERO_DWORD(dword); 236 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE); 237 238 if (value == MC_STATUS_DWORD_ASSERT) 239 return (EINTR); 240 else 241 return (EIO); 242} 243 244 __checkReturn boolean_t 245efx_mcdi_request_poll( 246 __in efx_nic_t *enp) 247{ 248 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 249 efx_mcdi_req_t *emrp; 250 efx_dword_t dword; 251 unsigned int pdur; 252 unsigned int seq; 253 unsigned int length; 254 int state; 255 int rc; 256 257 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 258 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 259 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 260 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 261 262 /* Serialise against post-watchdog efx_mcdi_ev* */ 263 EFSYS_LOCK(enp->en_eslp, state); 264 265 EFSYS_ASSERT(emip->emi_pending_req != NULL); 266 EFSYS_ASSERT(!emip->emi_ev_cpl); 267 emrp = emip->emi_pending_req; 268 269 /* Check for reboot atomically w.r.t efx_mcdi_request_start */ 270 if (emip->emi_poll_cnt++ == 0) { 271 if ((rc = efx_mcdi_poll_reboot(enp)) != 0) { 272 emip->emi_pending_req = NULL; 273 EFSYS_UNLOCK(enp->en_eslp, state); 274 275 goto fail1; 276 } 277 } 278 279 EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2); 280 pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST; 281 282 /* Read the command header */ 283 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE); 284 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) { 285 EFSYS_UNLOCK(enp->en_eslp, state); 286 return (B_FALSE); 287 } 288 289 /* Request complete */ 290 emip->emi_pending_req = NULL; 291 seq = (emip->emi_seq - 1) & 0xf; 292 293 /* Check for synchronous reboot */ 294 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 && 295 EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) { 296 /* Consume status word */ 297 EFSYS_SPIN(MCDI_STATUS_SLEEP_US); 298 efx_mcdi_poll_reboot(enp); 299 EFSYS_UNLOCK(enp->en_eslp, state); 300 rc = EIO; 301 goto fail2; 302 } 303 304 EFSYS_UNLOCK(enp->en_eslp, state); 305 306 /* Check that the returned data is consistent */ 307 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd || 308 EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) { 309 /* Response is for a different request */ 310 rc = EIO; 311 goto fail3; 312 } 313 314 length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN); 315 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) { 316 efx_dword_t errdword; 317 int errcode; 318 319 EFSYS_ASSERT3U(length, ==, 4); 320 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, 321 pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2), 322 &errdword, B_FALSE); 323 errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0); 324 rc = efx_mcdi_request_errcode(errcode); 325 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode); 326 goto fail4; 327 328 } else { 329 emrp->emr_out_length_used = length; 330 emrp->emr_rc = 0; 331 efx_mcdi_request_copyout(enp, emrp); 332 } 333 334 goto out; 335 336fail4: 337 EFSYS_PROBE(fail4); 338fail3: 339 EFSYS_PROBE(fail3); 340fail2: 341 EFSYS_PROBE(fail2); 342fail1: 343 EFSYS_PROBE1(fail1, int, rc); 344 345 /* Fill out error state */ 346 emrp->emr_rc = rc; 347 emrp->emr_out_length_used = 0; 348 349 /* Reboot/Assertion */ 350 if (rc == EIO || rc == EINTR) 351 efx_mcdi_raise_exception(enp, emrp, rc); 352 353out: 354 return (B_TRUE); 355} 356 357 void 358efx_mcdi_execute( 359 __in efx_nic_t *enp, 360 __in efx_mcdi_req_t *emrp) 361{ 362 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 363 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 364 365 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 366 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 367 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 368 369 emtp->emt_execute(emtp->emt_context, emrp); 370} 371 372 void 373efx_mcdi_ev_cpl( 374 __in efx_nic_t *enp, 375 __in unsigned int seq, 376 __in unsigned int outlen, 377 __in int errcode) 378{ 379 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 380 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 381 efx_mcdi_req_t *emrp; 382 int state; 383 384 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 385 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 386 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 387 388 /* 389 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start() 390 * when we're completing an aborted request. 391 */ 392 EFSYS_LOCK(enp->en_eslp, state); 393 if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl || 394 (seq != ((emip->emi_seq - 1) & 0xf))) { 395 EFSYS_ASSERT(emip->emi_aborted > 0); 396 if (emip->emi_aborted > 0) 397 --emip->emi_aborted; 398 EFSYS_UNLOCK(enp->en_eslp, state); 399 return; 400 } 401 402 emrp = emip->emi_pending_req; 403 emip->emi_pending_req = NULL; 404 EFSYS_UNLOCK(enp->en_eslp, state); 405 406 /* 407 * Fill out the remaining hdr fields, and copyout the payload 408 * if the user supplied an output buffer. 409 */ 410 if (errcode != 0) { 411 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, 412 int, errcode); 413 emrp->emr_out_length_used = 0; 414 emrp->emr_rc = efx_mcdi_request_errcode(errcode); 415 } else { 416 emrp->emr_out_length_used = outlen; 417 emrp->emr_rc = 0; 418 efx_mcdi_request_copyout(enp, emrp); 419 } 420 421 emtp->emt_ev_cpl(emtp->emt_context); 422} 423 424 void 425efx_mcdi_ev_death( 426 __in efx_nic_t *enp, 427 __in int rc) 428{ 429 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 430 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 431 efx_mcdi_req_t *emrp = NULL; 432 boolean_t ev_cpl; 433 int state; 434 435 /* 436 * The MCDI request (if there is one) has been terminated, either 437 * by a BADASSERT or REBOOT event. 438 * 439 * If there is an oustanding event-completed MCDI operation, then we 440 * will never receive the completion event (because both MCDI 441 * completions and BADASSERT events are sent to the same evq). So 442 * complete this MCDI op. 443 * 444 * This function might run in parallel with efx_mcdi_request_poll() 445 * for poll completed mcdi requests, and also with 446 * efx_mcdi_request_start() for post-watchdog completions. 447 */ 448 EFSYS_LOCK(enp->en_eslp, state); 449 emrp = emip->emi_pending_req; 450 ev_cpl = emip->emi_ev_cpl; 451 if (emrp != NULL && emip->emi_ev_cpl) { 452 emip->emi_pending_req = NULL; 453 454 emrp->emr_out_length_used = 0; 455 emrp->emr_rc = rc; 456 ++emip->emi_aborted; 457 } 458 459 /* Since we're running in parallel with a request, consume the 460 * status word before dropping the lock. 461 */ 462 if (rc == EIO || rc == EINTR) { 463 EFSYS_SPIN(MCDI_STATUS_SLEEP_US); 464 (void) efx_mcdi_poll_reboot(enp); 465 } 466 467 EFSYS_UNLOCK(enp->en_eslp, state); 468 469 efx_mcdi_raise_exception(enp, emrp, rc); 470 471 if (emrp != NULL && ev_cpl) 472 emtp->emt_ev_cpl(emtp->emt_context); 473} 474 475 __checkReturn int 476efx_mcdi_version( 477 __in efx_nic_t *enp, 478 __out_ecount_opt(4) uint16_t versionp[4], 479 __out_opt uint32_t *buildp, 480 __out_opt efx_mcdi_boot_t *statusp) 481{ 482 uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN, 483 MC_CMD_GET_BOOT_STATUS_OUT_LEN)]; 484 efx_mcdi_req_t req; 485 efx_word_t *ver_words; 486 uint16_t version[4]; 487 uint32_t build; 488 efx_mcdi_boot_t status; 489 int rc; 490 491 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 492 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 493 494 EFX_STATIC_ASSERT(MC_CMD_GET_VERSION_IN_LEN == 0); 495 req.emr_cmd = MC_CMD_GET_VERSION; 496 req.emr_in_buf = NULL; 497 req.emr_in_length = 0; 498 req.emr_out_buf = outbuf; 499 req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN; 500 501 efx_mcdi_execute(enp, &req); 502 503 if (req.emr_rc != 0) { 504 rc = req.emr_rc; 505 goto fail1; 506 } 507 508 /* bootrom support */ 509 if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) { 510 version[0] = version[1] = version[2] = version[3] = 0; 511 build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE); 512 513 goto version; 514 } 515 516 if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) { 517 rc = EMSGSIZE; 518 goto fail2; 519 } 520 521 ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION); 522 version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0); 523 version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0); 524 version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0); 525 version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0); 526 build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE); 527 528version: 529 /* The bootrom doesn't understand BOOT_STATUS */ 530 if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM) { 531 status = EFX_MCDI_BOOT_ROM; 532 goto out; 533 } 534 535 req.emr_cmd = MC_CMD_GET_BOOT_STATUS; 536 EFX_STATIC_ASSERT(MC_CMD_GET_BOOT_STATUS_IN_LEN == 0); 537 req.emr_in_buf = NULL; 538 req.emr_in_length = 0; 539 req.emr_out_buf = outbuf; 540 req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN; 541 542 efx_mcdi_execute(enp, &req); 543 544 if (req.emr_rc != 0) { 545 rc = req.emr_rc; 546 goto fail3; 547 } 548 549 if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) { 550 rc = EMSGSIZE; 551 goto fail4; 552 } 553 554 if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS, 555 GET_BOOT_STATUS_OUT_FLAGS_PRIMARY)) 556 status = EFX_MCDI_BOOT_PRIMARY; 557 else 558 status = EFX_MCDI_BOOT_SECONDARY; 559 560out: 561 if (versionp != NULL) 562 memcpy(versionp, version, sizeof (version)); 563 if (buildp != NULL) 564 *buildp = build; 565 if (statusp != NULL) 566 *statusp = status; 567 568 return (0); 569 570fail4: 571 EFSYS_PROBE(fail4); 572fail3: 573 EFSYS_PROBE(fail3); 574fail2: 575 EFSYS_PROBE(fail2); 576fail1: 577 EFSYS_PROBE1(fail1, int, rc); 578 579 return (rc); 580} 581 582 __checkReturn int 583efx_mcdi_init( 584 __in efx_nic_t *enp, 585 __in const efx_mcdi_transport_t *mtp) 586{ 587 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 588 efx_oword_t oword; 589 unsigned int portnum; 590 int rc; 591 592 EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0); 593 enp->en_mod_flags |= EFX_MOD_MCDI; 594 595 if (enp->en_family == EFX_FAMILY_FALCON) 596 return (0); 597 598 emip->emi_mtp = mtp; 599 600 /* Determine the port number to use for MCDI */ 601 EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword); 602 portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM); 603 604 if (portnum == 0) { 605 /* Presumably booted from ROM; only MCDI port 1 will work */ 606 emip->emi_port = 1; 607 } else if (portnum <= 2) { 608 emip->emi_port = portnum; 609 } else { 610 rc = EINVAL; 611 goto fail1; 612 } 613 614 /* 615 * Wipe the atomic reboot status so subsequent MCDI requests succeed. 616 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the 617 * assertion handler. 618 */ 619 (void) efx_mcdi_poll_reboot(enp); 620 621 return (0); 622 623fail1: 624 EFSYS_PROBE1(fail1, int, rc); 625 626 enp->en_mod_flags &= ~EFX_MOD_MCDI; 627 628 return (rc); 629} 630 631 632 __checkReturn int 633efx_mcdi_reboot( 634 __in efx_nic_t *enp) 635{ 636 uint8_t payload[MC_CMD_REBOOT_IN_LEN]; 637 efx_mcdi_req_t req; 638 int rc; 639 640 /* 641 * We could require the caller to have caused en_mod_flags=0 to 642 * call this function. This doesn't help the other port though, 643 * who's about to get the MC ripped out from underneath them. 644 * Since they have to cope with the subsequent fallout of MCDI 645 * failures, we should as well. 646 */ 647 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 648 649 req.emr_cmd = MC_CMD_REBOOT; 650 req.emr_in_buf = payload; 651 req.emr_in_length = MC_CMD_REBOOT_IN_LEN; 652 req.emr_out_buf = NULL; 653 req.emr_out_length = 0; 654 655 MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0); 656 657 efx_mcdi_execute(enp, &req); 658 659 /* Invert EIO */ 660 if (req.emr_rc != EIO) { 661 rc = EIO; 662 goto fail1; 663 } 664 665 return (0); 666 667fail1: 668 EFSYS_PROBE1(fail1, int, rc); 669 670 return (rc); 671} 672 673 __checkReturn boolean_t 674efx_mcdi_request_abort( 675 __in efx_nic_t *enp) 676{ 677 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 678 efx_mcdi_req_t *emrp; 679 boolean_t aborted; 680 int state; 681 682 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 683 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 684 685 /* 686 * efx_mcdi_ev_* may have already completed this event, and be 687 * spinning/blocked on the upper layer lock. So it *is* legitimate 688 * to for emi_pending_req to be NULL. If there is a pending event 689 * completed request, then provide a "credit" to allow 690 * efx_mcdi_ev_cpl() to accept a single spurious completion. 691 */ 692 EFSYS_LOCK(enp->en_eslp, state); 693 emrp = emip->emi_pending_req; 694 aborted = (emrp != NULL); 695 if (aborted) { 696 emip->emi_pending_req = NULL; 697 698 /* Error the request */ 699 emrp->emr_out_length_used = 0; 700 emrp->emr_rc = ETIMEDOUT; 701 702 /* Provide a credit for seqno/emr_pending_req mismatches */ 703 if (emip->emi_ev_cpl) 704 ++emip->emi_aborted; 705 706 /* 707 * The upper layer has called us, so we don't 708 * need to complete the request. 709 */ 710 } 711 EFSYS_UNLOCK(enp->en_eslp, state); 712 713 return (aborted); 714} 715 716 void 717efx_mcdi_fini( 718 __in efx_nic_t *enp) 719{ 720 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 721 722 EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI); 723 enp->en_mod_flags &= ~EFX_MOD_MCDI; 724 725 if (~(enp->en_features) & EFX_FEATURE_MCDI) 726 return; 727 728 emip->emi_mtp = NULL; 729 emip->emi_port = 0; 730 emip->emi_aborted = 0; 731} 732 733#endif /* EFSYS_OPT_MCDI */ 734