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