ef10_mcdi.c revision 291585
1/*- 2 * Copyright (c) 2012-2015 Solarflare Communications Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * The views and conclusions contained in the software and documentation are 27 * those of the authors and should not be interpreted as representing official 28 * policies, either expressed or implied, of the FreeBSD Project. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: head/sys/dev/sfxge/common/hunt_mcdi.c 291585 2015-12-01 15:26:46Z arybchik $"); 33 34#include "efsys.h" 35#include "efx.h" 36#include "efx_impl.h" 37 38 39#if EFSYS_OPT_HUNTINGTON 40 41#if EFSYS_OPT_MCDI 42 43#ifndef WITH_MCDI_V2 44#error "WITH_MCDI_V2 required for Huntington MCDIv2 commands." 45#endif 46 47typedef enum efx_mcdi_header_type_e { 48 EFX_MCDI_HEADER_TYPE_V1, /* MCDIv0 (BootROM), MCDIv1 commands */ 49 EFX_MCDI_HEADER_TYPE_V2, /* MCDIv2 commands */ 50} efx_mcdi_header_type_t; 51 52/* 53 * Return the header format to use for sending an MCDI request. 54 * 55 * An MCDIv1 (Siena compatible) command should use MCDIv2 encapsulation if the 56 * request input buffer or response output buffer are too large for the MCDIv1 57 * format. An MCDIv2 command must always be sent using MCDIv2 encapsulation. 58 */ 59#define EFX_MCDI_HEADER_TYPE(_cmd, _length) \ 60 ((((_cmd) & ~EFX_MASK32(MCDI_HEADER_CODE)) || \ 61 ((_length) & ~EFX_MASK32(MCDI_HEADER_DATALEN))) ? \ 62 EFX_MCDI_HEADER_TYPE_V2 : EFX_MCDI_HEADER_TYPE_V1) 63 64 65/* 66 * MCDI Header NOT_EPOCH flag 67 * ========================== 68 * A new epoch begins at initial startup or after an MC reboot, and defines when 69 * the MC should reject stale MCDI requests. 70 * 71 * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all 72 * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1. 73 * 74 * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a 75 * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0. 76 */ 77 78 79 __checkReturn efx_rc_t 80hunt_mcdi_init( 81 __in efx_nic_t *enp, 82 __in const efx_mcdi_transport_t *emtp) 83{ 84 efsys_mem_t *esmp = emtp->emt_dma_mem; 85 efx_dword_t dword; 86 efx_rc_t rc; 87 88 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON); 89 EFSYS_ASSERT(enp->en_features & EFX_FEATURE_MCDI_DMA); 90 91 /* A host DMA buffer is required for Huntington MCDI */ 92 if (esmp == NULL) { 93 rc = EINVAL; 94 goto fail1; 95 } 96 97 /* 98 * Ensure that the MC doorbell is in a known state before issuing MCDI 99 * commands. The recovery algorithm requires that the MC command buffer 100 * must be 256 byte aligned. See bug24769. 101 */ 102 if ((EFSYS_MEM_ADDR(esmp) & 0xFF) != 0) { 103 rc = EINVAL; 104 goto fail2; 105 } 106 EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 1); 107 EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE); 108 109 /* Save initial MC reboot status */ 110 (void) hunt_mcdi_poll_reboot(enp); 111 112 /* Start a new epoch (allow fresh MCDI requests to succeed) */ 113 efx_mcdi_new_epoch(enp); 114 115 return (0); 116 117fail2: 118 EFSYS_PROBE(fail2); 119fail1: 120 EFSYS_PROBE1(fail1, efx_rc_t, rc); 121 122 return (rc); 123} 124 125 void 126hunt_mcdi_fini( 127 __in efx_nic_t *enp) 128{ 129 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 130 131 emip->emi_new_epoch = B_FALSE; 132} 133 134 void 135hunt_mcdi_request_copyin( 136 __in efx_nic_t *enp, 137 __in efx_mcdi_req_t *emrp, 138 __in unsigned int seq, 139 __in boolean_t ev_cpl, 140 __in boolean_t new_epoch) 141{ 142 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 143 efsys_mem_t *esmp = emtp->emt_dma_mem; 144 efx_mcdi_header_type_t hdr_type; 145 efx_dword_t dword; 146 unsigned int xflags; 147 unsigned int pos; 148 size_t offset; 149 150 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON); 151 152 xflags = 0; 153 if (ev_cpl) 154 xflags |= MCDI_HEADER_XFLAGS_EVREQ; 155 156 offset = 0; 157 158 hdr_type = EFX_MCDI_HEADER_TYPE(emrp->emr_cmd, 159 MAX(emrp->emr_in_length, emrp->emr_out_length)); 160 161 if (hdr_type == EFX_MCDI_HEADER_TYPE_V2) { 162 /* Construct MCDI v2 header */ 163 EFX_POPULATE_DWORD_8(dword, 164 MCDI_HEADER_CODE, MC_CMD_V2_EXTN, 165 MCDI_HEADER_RESYNC, 1, 166 MCDI_HEADER_DATALEN, 0, 167 MCDI_HEADER_SEQ, seq, 168 MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1, 169 MCDI_HEADER_ERROR, 0, 170 MCDI_HEADER_RESPONSE, 0, 171 MCDI_HEADER_XFLAGS, xflags); 172 EFSYS_MEM_WRITED(esmp, offset, &dword); 173 offset += sizeof (dword); 174 175 EFX_POPULATE_DWORD_2(dword, 176 MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd, 177 MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length); 178 EFSYS_MEM_WRITED(esmp, offset, &dword); 179 offset += sizeof (dword); 180 } else { 181 /* Construct MCDI v1 header */ 182 EFX_POPULATE_DWORD_8(dword, 183 MCDI_HEADER_CODE, emrp->emr_cmd, 184 MCDI_HEADER_RESYNC, 1, 185 MCDI_HEADER_DATALEN, emrp->emr_in_length, 186 MCDI_HEADER_SEQ, seq, 187 MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1, 188 MCDI_HEADER_ERROR, 0, 189 MCDI_HEADER_RESPONSE, 0, 190 MCDI_HEADER_XFLAGS, xflags); 191 EFSYS_MEM_WRITED(esmp, offset, &dword); 192 offset += sizeof (dword); 193 } 194 195 /* Construct the payload */ 196 for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) { 197 memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos), 198 MIN(sizeof (dword), emrp->emr_in_length - pos)); 199 EFSYS_MEM_WRITED(esmp, offset + pos, &dword); 200 } 201 202 /* Ring the doorbell to post the command DMA address to the MC */ 203 EFSYS_ASSERT((EFSYS_MEM_ADDR(esmp) & 0xFF) == 0); 204 205 /* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */ 206 EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, offset + emrp->emr_in_length); 207 EFSYS_PIO_WRITE_BARRIER(); 208 209 EFX_POPULATE_DWORD_1(dword, 210 EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) >> 32); 211 EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE); 212 213 EFX_POPULATE_DWORD_1(dword, 214 EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) & 0xffffffff); 215 EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE); 216} 217 218 void 219hunt_mcdi_request_copyout( 220 __in efx_nic_t *enp, 221 __in efx_mcdi_req_t *emrp) 222{ 223 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 224 efsys_mem_t *esmp = emtp->emt_dma_mem; 225 unsigned int pos; 226 unsigned int offset; 227 efx_dword_t hdr; 228 efx_dword_t hdr2; 229 efx_dword_t data; 230 size_t bytes; 231 232 if (emrp->emr_out_buf == NULL) 233 return; 234 235 /* Read the command header to detect MCDI response format */ 236 EFSYS_MEM_READD(esmp, 0, &hdr); 237 if (EFX_DWORD_FIELD(hdr, MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) { 238 offset = 2 * sizeof (efx_dword_t); 239 240 /* 241 * Read the actual payload length. The length given in the event 242 * is only correct for responses with the V1 format. 243 */ 244 EFSYS_MEM_READD(esmp, sizeof (efx_dword_t), &hdr2); 245 emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr2, 246 MC_CMD_V2_EXTN_IN_ACTUAL_LEN); 247 } else { 248 offset = sizeof (efx_dword_t); 249 } 250 251 /* Copy payload out into caller supplied buffer */ 252 bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length); 253 for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) { 254 EFSYS_MEM_READD(esmp, offset + pos, &data); 255 memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data, 256 MIN(sizeof (data), bytes - pos)); 257 } 258} 259 260 __checkReturn boolean_t 261hunt_mcdi_request_poll( 262 __in efx_nic_t *enp) 263{ 264 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 265 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 266 efsys_mem_t *esmp = emtp->emt_dma_mem; 267 efx_mcdi_req_t *emrp; 268 efx_dword_t dword; 269 unsigned int seq; 270 unsigned int cmd; 271 unsigned int length; 272 size_t offset; 273 int state; 274 efx_rc_t rc; 275 276 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON); 277 278 /* Serialise against post-watchdog efx_mcdi_ev* */ 279 EFSYS_LOCK(enp->en_eslp, state); 280 281 EFSYS_ASSERT(emip->emi_pending_req != NULL); 282 EFSYS_ASSERT(!emip->emi_ev_cpl); 283 emrp = emip->emi_pending_req; 284 285 offset = 0; 286 287 /* Read the command header */ 288 EFSYS_MEM_READD(esmp, offset, &dword); 289 offset += sizeof (efx_dword_t); 290 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) { 291 EFSYS_UNLOCK(enp->en_eslp, state); 292 return (B_FALSE); 293 } 294 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) { 295 efx_dword_t dword2; 296 297 EFSYS_MEM_READD(esmp, offset, &dword2); 298 offset += sizeof (efx_dword_t); 299 300 cmd = EFX_DWORD_FIELD(dword2, MC_CMD_V2_EXTN_IN_EXTENDED_CMD); 301 length = EFX_DWORD_FIELD(dword2, MC_CMD_V2_EXTN_IN_ACTUAL_LEN); 302 } else { 303 cmd = EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE); 304 length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN); 305 } 306 307 /* Request complete */ 308 emip->emi_pending_req = NULL; 309 seq = (emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ); 310 311 /* Check for synchronous reboot */ 312 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 && length == 0) { 313 /* The MC has rebooted since the request was sent. */ 314 EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US); 315 hunt_mcdi_poll_reboot(enp); 316 317 EFSYS_UNLOCK(enp->en_eslp, state); 318 rc = EIO; 319 goto fail1; 320 } 321 322 /* Ensure stale MCDI requests fail after an MC reboot. */ 323 emip->emi_new_epoch = B_FALSE; 324 325 EFSYS_UNLOCK(enp->en_eslp, state); 326 327 /* Check that the returned data is consistent */ 328 if (cmd != emrp->emr_cmd || 329 EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) { 330 /* Response is for a different request */ 331 rc = EIO; 332 goto fail2; 333 } 334 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) { 335 efx_dword_t errdword; 336 int errcode; 337 int argnum; 338 339 /* Read error code (and arg num for MCDI v2 commands) */ 340 EFSYS_MEM_READD(esmp, offset + MC_CMD_ERR_CODE_OFST, &errdword); 341 errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0); 342 343 EFSYS_MEM_READD(esmp, offset + MC_CMD_ERR_ARG_OFST, &errdword); 344 argnum = EFX_DWORD_FIELD(errdword, EFX_DWORD_0); 345 346 rc = efx_mcdi_request_errcode(errcode); 347 if (!emrp->emr_quiet) { 348 EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd, 349 int, errcode, int, argnum); 350 } 351 goto fail3; 352 353 } else { 354 emrp->emr_out_length_used = length; 355 emrp->emr_rc = 0; 356 hunt_mcdi_request_copyout(enp, emrp); 357 } 358 359 goto out; 360 361fail3: 362 if (!emrp->emr_quiet) 363 EFSYS_PROBE(fail3); 364fail2: 365 if (!emrp->emr_quiet) 366 EFSYS_PROBE(fail2); 367fail1: 368 if (!emrp->emr_quiet) 369 EFSYS_PROBE1(fail1, efx_rc_t, rc); 370 371 /* Fill out error state */ 372 emrp->emr_rc = rc; 373 emrp->emr_out_length_used = 0; 374 375 /* Reboot/Assertion */ 376 if (rc == EIO || rc == EINTR) 377 efx_mcdi_raise_exception(enp, emrp, rc); 378 379out: 380 return (B_TRUE); 381} 382 383 efx_rc_t 384hunt_mcdi_poll_reboot( 385 __in efx_nic_t *enp) 386{ 387 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 388 efx_dword_t dword; 389 uint32_t old_status; 390 uint32_t new_status; 391 efx_rc_t rc; 392 393 old_status = emip->emi_mc_reboot_status; 394 395 /* Update MC reboot status word */ 396 EFX_BAR_TBL_READD(enp, ER_DZ_BIU_MC_SFT_STATUS_REG, 0, &dword, B_FALSE); 397 new_status = dword.ed_u32[0]; 398 399 /* MC has rebooted if the value has changed */ 400 if (new_status != old_status) { 401 emip->emi_mc_reboot_status = new_status; 402 403 /* 404 * FIXME: Ignore detected MC REBOOT for now. 405 * 406 * The Siena support for checking for MC reboot from status 407 * flags is broken - see comments in siena_mcdi_poll_reboot(). 408 * As the generic MCDI code is shared the Huntington reboot 409 * detection suffers similar problems. 410 * 411 * Do not report an error when the boot status changes until 412 * this can be handled by common code drivers (and reworked to 413 * support Siena too). 414 */ 415 if (B_FALSE) { 416 rc = EIO; 417 goto fail1; 418 } 419 } 420 421 return (0); 422 423fail1: 424 EFSYS_PROBE1(fail1, efx_rc_t, rc); 425 426 return (rc); 427} 428 429 __checkReturn efx_rc_t 430hunt_mcdi_fw_update_supported( 431 __in efx_nic_t *enp, 432 __out boolean_t *supportedp) 433{ 434 efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 435 436 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON); 437 438 /* 439 * Use privilege mask state at MCDI attach. 440 * Admin privilege must be used prior to introduction of 441 * specific flag. 442 */ 443 *supportedp = (encp->enc_privilege_mask & 444 MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN) 445 == MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN; 446 447 return (0); 448} 449 450 __checkReturn efx_rc_t 451hunt_mcdi_macaddr_change_supported( 452 __in efx_nic_t *enp, 453 __out boolean_t *supportedp) 454{ 455 efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 456 uint32_t privilege_mask = encp->enc_privilege_mask; 457 458 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON); 459 460 /* 461 * Use privilege mask state at MCDI attach. 462 * Admin privilege must be used prior to introduction of 463 * specific flag (at v4.6). 464 */ 465 *supportedp = 466 ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING) == 467 MC_CMD_PRIVILEGE_MASK_IN_GRP_MAC_SPOOFING) || 468 ((privilege_mask & MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN) == 469 MC_CMD_PRIVILEGE_MASK_IN_GRP_ADMIN); 470 471 return (0); 472} 473 474#endif /* EFSYS_OPT_MCDI */ 475 476#endif /* EFSYS_OPT_HUNTINGTON */ 477