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 /* 39 * A reboot/assertion causes the MCDI status word to be set after the 40 * command word is set or a REBOOT event is sent. If we notice a reboot 41 * via these mechanisms then wait 10ms for the status word to be set. 42 */ 43 #define MCDI_STATUS_SLEEP_US 10000 44 45 void 46 efx_mcdi_request_start( 47 __in efx_nic_t *enp, 48 __in efx_mcdi_req_t *emrp, 49 __in boolean_t ev_cpl) 50 { 51 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 52 efx_dword_t dword; 53 unsigned int seq; 54 unsigned int xflags; 55 unsigned int pdur; 56 unsigned int dbr; 57 unsigned int pos; 58 int state; 59 60 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 61 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 62 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 63 64 switch (emip->emi_port) { 65 case 1: 66 pdur = MC_SMEM_P0_PDU_OFST >> 2; 67 dbr = MC_SMEM_P0_DOORBELL_OFST >> 2; 68 break; 69 case 2: 70 pdur = MC_SMEM_P1_PDU_OFST >> 2; 71 dbr = MC_SMEM_P1_DOORBELL_OFST >> 2; 72 break; 73 default: 74 EFSYS_ASSERT(0); 75 pdur = dbr = 0; 76 }; 77 78 /* 79 * efx_mcdi_request_start() is naturally serialised against both 80 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(), 81 * by virtue of there only being one outstanding MCDI request. 82 * Unfortunately, upper layers may also call efx_mcdi_request_abort() 83 * at any time, to timeout a pending mcdi request, That request may 84 * then subsequently complete, meaning efx_mcdi_ev_cpl() or 85 * efx_mcdi_ev_death() may end up running in parallel with 86 * efx_mcdi_request_start(). This race is handled by ensuring that 87 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the 88 * en_eslp lock. 89 */ 90 EFSYS_LOCK(enp->en_eslp, state); 91 EFSYS_ASSERT(emip->emi_pending_req == NULL); 92 emip->emi_pending_req = emrp; 93 emip->emi_ev_cpl = ev_cpl; 94 emip->emi_poll_cnt = 0; 95 seq = emip->emi_seq++ & 0xf; 96 EFSYS_UNLOCK(enp->en_eslp, state); 97 98 xflags = 0; 99 if (ev_cpl) 100 xflags |= MCDI_HEADER_XFLAGS_EVREQ; 101 102 /* Construct the header in shared memory */ 103 EFX_POPULATE_DWORD_6(dword, 104 MCDI_HEADER_CODE, emrp->emr_cmd, 105 MCDI_HEADER_RESYNC, 1, 106 MCDI_HEADER_DATALEN, emrp->emr_in_length, 107 MCDI_HEADER_SEQ, seq, 108 MCDI_HEADER_RESPONSE, 0, 109 MCDI_HEADER_XFLAGS, xflags); 110 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE); 111 112 for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) { 113 memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos), 114 MIN(sizeof (dword), emrp->emr_in_length - pos)); 115 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, 116 pdur + 1 + (pos >> 2), &dword, B_FALSE); 117 } 118 119 /* Ring the doorbell */ 120 EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11); 121 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE); 122 } 123 124 static void 125 efx_mcdi_request_copyout( 126 __in efx_nic_t *enp, 127 __in efx_mcdi_req_t *emrp) 128 { 129 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 130 unsigned int pos; 131 unsigned int pdur; 132 efx_dword_t data; 133 134 pdur = (emip->emi_port == 1) 135 ? MC_SMEM_P0_PDU_OFST >> 2 136 : MC_SMEM_P1_PDU_OFST >> 2; 137 138 /* Copy payload out if caller supplied buffer */ 139 if (emrp->emr_out_buf != NULL) { 140 size_t bytes = MIN(emrp->emr_out_length_used, 141 emrp->emr_out_length); 142 for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) { 143 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, 144 pdur + 1 + (pos >> 2), &data, B_FALSE); 145 memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data, 146 MIN(sizeof (data), bytes - pos)); 147 } 148 } 149 } 150 151 static int 152 efx_mcdi_request_errcode( 153 __in unsigned int err) 154 { 155 156 switch (err) { 157 case MC_CMD_ERR_ENOENT: 158 return (ENOENT); 159 case MC_CMD_ERR_EINTR: 160 return (EINTR); 161 case MC_CMD_ERR_EACCES: 162 return (EACCES); 163 case MC_CMD_ERR_EBUSY: 164 return (EBUSY); 165 case MC_CMD_ERR_EINVAL: 166 return (EINVAL); 167 case MC_CMD_ERR_EDEADLK: 168 return (EDEADLK); 169 case MC_CMD_ERR_ENOSYS: 170 return (ENOTSUP); 171 case MC_CMD_ERR_ETIME: 172 return (ETIMEDOUT); 173 #ifdef WITH_MCDI_V2 174 case MC_CMD_ERR_EAGAIN: 175 return (EAGAIN); 176 case MC_CMD_ERR_ENOSPC: 177 return (ENOSPC); 178 #endif 179 default: 180 EFSYS_PROBE1(mc_pcol_error, int, err); 181 return (EIO); 182 } 183 } 184 185 static void 186 efx_mcdi_raise_exception( 187 __in efx_nic_t *enp, 188 __in_opt efx_mcdi_req_t *emrp, 189 __in int rc) 190 { 191 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 192 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 193 efx_mcdi_exception_t exception; 194 195 /* Reboot or Assertion failure only */ 196 EFSYS_ASSERT(rc == EIO || rc == EINTR); 197 198 /* 199 * If MC_CMD_REBOOT causes a reboot (dependent on parameters), 200 * then the EIO is not worthy of an exception. 201 */ 202 if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO) 203 return; 204 205 exception = (rc == EIO) 206 ? EFX_MCDI_EXCEPTION_MC_REBOOT 207 : EFX_MCDI_EXCEPTION_MC_BADASSERT; 208 209 emtp->emt_exception(emtp->emt_context, exception); 210 } 211 212 static int 213 efx_mcdi_poll_reboot( 214 __in efx_nic_t *enp) 215 { 216 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 217 unsigned int rebootr; 218 efx_dword_t dword; 219 uint32_t value; 220 221 EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2); 222 rebootr = ((emip->emi_port == 1) 223 ? MC_SMEM_P0_STATUS_OFST >> 2 224 : MC_SMEM_P1_STATUS_OFST >> 2); 225 226 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE); 227 value = EFX_DWORD_FIELD(dword, EFX_DWORD_0); 228 229 if (value == 0) 230 return (0); 231 232 EFX_ZERO_DWORD(dword); 233 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE); 234 235 if (value == MC_STATUS_DWORD_ASSERT) 236 return (EINTR); 237 else 238 return (EIO); 239 } 240 241 __checkReturn boolean_t 242 efx_mcdi_request_poll( 243 __in efx_nic_t *enp) 244 { 245 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 246 efx_mcdi_req_t *emrp; 247 efx_dword_t dword; 248 unsigned int pdur; 249 unsigned int seq; 250 unsigned int length; 251 int state; 252 int rc; 253 254 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 255 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 256 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 257 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 258 259 /* Serialise against post-watchdog efx_mcdi_ev* */ 260 EFSYS_LOCK(enp->en_eslp, state); 261 262 EFSYS_ASSERT(emip->emi_pending_req != NULL); 263 EFSYS_ASSERT(!emip->emi_ev_cpl); 264 emrp = emip->emi_pending_req; 265 266 /* Check for reboot atomically w.r.t efx_mcdi_request_start */ 267 if (emip->emi_poll_cnt++ == 0) { 268 if ((rc = efx_mcdi_poll_reboot(enp)) != 0) { 269 emip->emi_pending_req = NULL; 270 EFSYS_UNLOCK(enp->en_eslp, state); 271 272 goto fail1; 273 } 274 } 275 276 EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2); 277 pdur = (emip->emi_port == 1) 278 ? MC_SMEM_P0_PDU_OFST >> 2 279 : MC_SMEM_P1_PDU_OFST >> 2; 280 281 /* Read the command header */ 282 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE); 283 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) { 284 EFSYS_UNLOCK(enp->en_eslp, state); 285 return (B_FALSE); 286 } 287 288 /* Request complete */ 289 emip->emi_pending_req = NULL; 290 seq = (emip->emi_seq - 1) & 0xf; 291 292 /* Check for synchronous reboot */ 293 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 && 294 EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) { 295 /* Consume status word */ 296 EFSYS_SPIN(MCDI_STATUS_SLEEP_US); 297 efx_mcdi_poll_reboot(enp); 298 EFSYS_UNLOCK(enp->en_eslp, state); 299 rc = EIO; 300 goto fail2; 301 } 302 303 EFSYS_UNLOCK(enp->en_eslp, state); 304 305 /* Check that the returned data is consistent */ 306 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd || 307 EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) { 308 /* Response is for a different request */ 309 rc = EIO; 310 goto fail3; 311 } 312 313 length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN); 314 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) { 315 efx_dword_t errdword; 316 int errcode; 317 318 EFSYS_ASSERT3U(length, ==, 4); 319 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, 320 pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2), 321 &errdword, B_FALSE); 322 errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0); 323 rc = efx_mcdi_request_errcode(errcode); 324 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode); 325 goto fail4; 326 327 } else { 328 emrp->emr_out_length_used = length; 329 emrp->emr_rc = 0; 330 efx_mcdi_request_copyout(enp, emrp); 331 } 332 333 goto out; 334 335 fail4: 336 EFSYS_PROBE(fail4); 337 fail3: 338 EFSYS_PROBE(fail3); 339 fail2: 340 EFSYS_PROBE(fail2); 341 fail1: 342 EFSYS_PROBE1(fail1, int, rc); 343 344 /* Fill out error state */ 345 emrp->emr_rc = rc; 346 emrp->emr_out_length_used = 0; 347 348 /* Reboot/Assertion */ 349 if (rc == EIO || rc == EINTR) 350 efx_mcdi_raise_exception(enp, emrp, rc); 351 352 out: 353 return (B_TRUE); 354 } 355 356 void 357 efx_mcdi_execute( 358 __in efx_nic_t *enp, 359 __in efx_mcdi_req_t *emrp) 360 { 361 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 362 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 363 364 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 365 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 366 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 367 368 emtp->emt_execute(emtp->emt_context, emrp); 369 } 370 371 void 372 efx_mcdi_ev_cpl( 373 __in efx_nic_t *enp, 374 __in unsigned int seq, 375 __in unsigned int outlen, 376 __in int errcode) 377 { 378 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 379 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 380 efx_mcdi_req_t *emrp; 381 int state; 382 383 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); 384 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 385 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); 386 387 /* 388 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start() 389 * when we're completing an aborted request. 390 */ 391 EFSYS_LOCK(enp->en_eslp, state); 392 if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl || 393 (seq != ((emip->emi_seq - 1) & 0xf))) { 394 EFSYS_ASSERT(emip->emi_aborted > 0); 395 if (emip->emi_aborted > 0) 396 --emip->emi_aborted; 397 EFSYS_UNLOCK(enp->en_eslp, state); 398 return; 399 } 400 401 emrp = emip->emi_pending_req; 402 emip->emi_pending_req = NULL; 403 EFSYS_UNLOCK(enp->en_eslp, state); 404 405 /* 406 * Fill out the remaining hdr fields, and copyout the payload 407 * if the user supplied an output buffer. 408 */ 409 if (errcode != 0) { 410 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, 411 int, errcode); 412 emrp->emr_out_length_used = 0; 413 emrp->emr_rc = efx_mcdi_request_errcode(errcode); 414 } else { 415 emrp->emr_out_length_used = outlen; 416 emrp->emr_rc = 0; 417 efx_mcdi_request_copyout(enp, emrp); 418 } 419 420 emtp->emt_ev_cpl(emtp->emt_context); 421 } 422 423 void 424 efx_mcdi_ev_death( 425 __in efx_nic_t *enp, 426 __in int rc) 427 { 428 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); 429 const efx_mcdi_transport_t *emtp = emip->emi_mtp; 430 efx_mcdi_req_t *emrp = NULL; 431 boolean_t ev_cpl; 432 int state; 433 434 /* 435 * The MCDI request (if there is one) has been terminated, either 436 * by a BADASSERT or REBOOT event. 437 * 438 * If there is an outstanding event-completed MCDI operation, then we 439 * will never receive the completion event (because both MCDI 440 * completions and BADASSERT events are sent to the same evq). So 441 * complete this MCDI op. 442 * 443 * This function might run in parallel with efx_mcdi_request_poll() 444 * for poll completed mcdi requests, and also with 445 * efx_mcdi_request_start() for post-watchdog completions. 446 */ 447 EFSYS_LOCK(enp->en_eslp, state); 448 emrp = emip->emi_pending_req; 449 ev_cpl = emip->emi_ev_cpl; 450 if (emrp != NULL && emip->emi_ev_cpl) { 451 emip->emi_pending_req = NULL; 452 453 emrp->emr_out_length_used = 0; 454 emrp->emr_rc = rc; 455 ++emip->emi_aborted; 456 } 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 476 efx_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 528 version: 529 /* The bootrom doesn't understand BOOT_STATUS */ 530 if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_SIENA_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 560 out: 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 570 fail4: 571 EFSYS_PROBE(fail4); 572 fail3: 573 EFSYS_PROBE(fail3); 574 fail2: 575 EFSYS_PROBE(fail2); 576 fail1: 577 EFSYS_PROBE1(fail1, int, rc); 578 579 return (rc); 580 } 581 582 __checkReturn int 583 efx_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 623 fail1: 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 633 efx_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 667 fail1: 668 EFSYS_PROBE1(fail1, int, rc); 669 670 return (rc); 671 } 672 673 __checkReturn boolean_t 674 efx_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 717 efx_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