1 /* SPDX-License-Identifier: BSD-3-Clause 2 * 3 * Copyright(c) 2019-2020 Xilinx, Inc. 4 * Copyright(c) 2012-2019 Solarflare Communications Inc. 5 */ 6 7 #include "efx.h" 8 #include "efx_impl.h" 9 10 11 #if EFX_OPTS_EF10() 12 13 #if EFSYS_OPT_MCDI 14 15 #ifndef WITH_MCDI_V2 16 #error "WITH_MCDI_V2 required for EF10 MCDIv2 commands." 17 #endif 18 19 20 __checkReturn efx_rc_t 21 ef10_mcdi_init( 22 __in efx_nic_t *enp, 23 __in const efx_mcdi_transport_t *emtp) 24 { 25 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 26 efsys_mem_t *esmp = emtp->emt_dma_mem; 27 efx_dword_t dword; 28 efx_rc_t rc; 29 30 EFSYS_ASSERT(EFX_FAMILY_IS_EF10(enp)); 31 EFSYS_ASSERT(enp->en_features & EFX_FEATURE_MCDI_DMA); 32 33 /* 34 * All EF10 firmware supports MCDIv2 and MCDIv1. 35 * Medford BootROM supports MCDIv2 and MCDIv1. 36 * Huntington BootROM supports MCDIv1 only. 37 */ 38 emip->emi_max_version = 2; 39 40 /* A host DMA buffer is required for EF10 MCDI */ 41 if (esmp == NULL) { 42 rc = EINVAL; 43 goto fail1; 44 } 45 46 /* 47 * Ensure that the MC doorbell is in a known state before issuing MCDI 48 * commands. The recovery algorithm requires that the MC command buffer 49 * must be 256 byte aligned. See bug24769. 50 */ 51 if ((EFSYS_MEM_ADDR(esmp) & 0xFF) != 0) { 52 rc = EINVAL; 53 goto fail2; 54 } 55 EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 1); 56 EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE); 57 58 /* Save initial MC reboot status */ 59 (void) ef10_mcdi_poll_reboot(enp); 60 61 /* Start a new epoch (allow fresh MCDI requests to succeed) */ 62 efx_mcdi_new_epoch(enp); 63 64 return (0); 65 66 fail2: 67 EFSYS_PROBE(fail2); 68 fail1: 69 EFSYS_PROBE1(fail1, efx_rc_t, rc); 70 71 return (rc); 72 } 73 74 void 75 ef10_mcdi_fini( 76 __in efx_nic_t *enp) 77 { 78 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 79 80 emip->emi_new_epoch = B_FALSE; 81 } 82 83 /* 84 * In older firmware all commands are processed in a single thread, so a long 85 * running command for one PCIe function can block processing for another 86 * function (see bug 61269). 87 * 88 * In newer firmware that supports multithreaded MCDI processing, we can extend 89 * the timeout for long-running requests which we know firmware may choose to 90 * process in a background thread. 91 */ 92 #define EF10_MCDI_CMD_TIMEOUT_US (10 * 1000 * 1000) 93 #define EF10_MCDI_CMD_LONG_TIMEOUT_US (60 * 1000 * 1000) 94 95 void 96 ef10_mcdi_get_timeout( 97 __in efx_nic_t *enp, 98 __in efx_mcdi_req_t *emrp, 99 __out uint32_t *timeoutp) 100 { 101 efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 102 103 switch (emrp->emr_cmd) { 104 case MC_CMD_POLL_BIST: 105 case MC_CMD_NVRAM_ERASE: 106 case MC_CMD_LICENSING_V3: 107 case MC_CMD_NVRAM_UPDATE_FINISH: 108 if (encp->enc_nvram_update_verify_result_supported != B_FALSE) { 109 /* 110 * Potentially longer running commands, which firmware 111 * may choose to process in a background thread. 112 */ 113 *timeoutp = EF10_MCDI_CMD_LONG_TIMEOUT_US; 114 break; 115 } 116 /* FALLTHRU */ 117 default: 118 *timeoutp = EF10_MCDI_CMD_TIMEOUT_US; 119 break; 120 } 121 } 122 123 void 124 ef10_mcdi_send_request( 125 __in efx_nic_t *enp, 126 __in_bcount(hdr_len) void *hdrp, 127 __in size_t hdr_len, 128 __in_bcount(sdu_len) void *sdup, 129 __in size_t sdu_len) 130 { 131 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 132 efsys_mem_t *esmp = emtp->emt_dma_mem; 133 efx_dword_t dword; 134 unsigned int pos; 135 136 EFSYS_ASSERT(EFX_FAMILY_IS_EF10(enp)); 137 138 /* Write the header */ 139 for (pos = 0; pos < hdr_len; pos += sizeof (efx_dword_t)) { 140 dword = *(efx_dword_t *)((uint8_t *)hdrp + pos); 141 EFSYS_MEM_WRITED(esmp, pos, &dword); 142 } 143 144 /* Write the payload */ 145 for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) { 146 dword = *(efx_dword_t *)((uint8_t *)sdup + pos); 147 EFSYS_MEM_WRITED(esmp, hdr_len + pos, &dword); 148 } 149 150 /* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */ 151 EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, hdr_len + sdu_len); 152 EFSYS_PIO_WRITE_BARRIER(); 153 154 /* Ring the doorbell to post the command DMA address to the MC */ 155 EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 156 EFSYS_MEM_ADDR(esmp) >> 32); 157 EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE); 158 159 EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 160 EFSYS_MEM_ADDR(esmp) & 0xffffffff); 161 EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE); 162 } 163 164 __checkReturn boolean_t 165 ef10_mcdi_poll_response( 166 __in efx_nic_t *enp) 167 { 168 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 169 efsys_mem_t *esmp = emtp->emt_dma_mem; 170 efx_dword_t hdr; 171 172 EFSYS_MEM_READD(esmp, 0, &hdr); 173 EFSYS_MEM_READ_BARRIER(); 174 175 return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE); 176 } 177 178 void 179 ef10_mcdi_read_response( 180 __in efx_nic_t *enp, 181 __out_bcount(length) void *bufferp, 182 __in size_t offset, 183 __in size_t length) 184 { 185 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; 186 efsys_mem_t *esmp = emtp->emt_dma_mem; 187 unsigned int pos = 0; 188 efx_dword_t data; 189 size_t remaining = length; 190 191 while (remaining > 0) { 192 size_t chunk = MIN(remaining, sizeof (data)); 193 194 EFSYS_MEM_READD(esmp, offset + pos, &data); 195 memcpy((uint8_t *)bufferp + pos, &data, chunk); 196 pos += chunk; 197 remaining -= chunk; 198 } 199 } 200 201 efx_rc_t 202 ef10_mcdi_poll_reboot( 203 __in efx_nic_t *enp) 204 { 205 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 206 efx_dword_t dword; 207 uint32_t old_status; 208 uint32_t new_status; 209 efx_rc_t rc; 210 211 old_status = emip->emi_mc_reboot_status; 212 213 /* Update MC reboot status word */ 214 EFX_BAR_TBL_READD(enp, ER_DZ_BIU_MC_SFT_STATUS_REG, 0, &dword, B_FALSE); 215 new_status = dword.ed_u32[0]; 216 217 /* MC has rebooted if the value has changed */ 218 if (new_status != old_status) { 219 emip->emi_mc_reboot_status = new_status; 220 221 /* 222 * FIXME: Ignore detected MC REBOOT for now. 223 * 224 * The Siena support for checking for MC reboot from status 225 * flags is broken - see comments in siena_mcdi_poll_reboot(). 226 * As the generic MCDI code is shared the EF10 reboot 227 * detection suffers similar problems. 228 * 229 * Do not report an error when the boot status changes until 230 * this can be handled by common code drivers (and reworked to 231 * support Siena too). 232 */ 233 _NOTE(CONSTANTCONDITION) 234 if (B_FALSE) { 235 rc = EIO; 236 goto fail1; 237 } 238 } 239 240 return (0); 241 242 fail1: 243 EFSYS_PROBE1(fail1, efx_rc_t, rc); 244 245 return (rc); 246 } 247 248 __checkReturn efx_rc_t 249 ef10_mcdi_feature_supported( 250 __in efx_nic_t *enp, 251 __in efx_mcdi_feature_id_t id, 252 __out boolean_t *supportedp) 253 { 254 efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 255 uint32_t privilege_mask = encp->enc_privilege_mask; 256 efx_rc_t rc; 257 258 EFSYS_ASSERT(EFX_FAMILY_IS_EF10(enp)); 259 260 /* 261 * Use privilege mask state at MCDI attach. 262 */ 263 264 switch (id) { 265 case EFX_MCDI_FEATURE_FW_UPDATE: 266 /* 267 * Admin privilege must be used prior to introduction of 268 * specific flag. 269 */ 270 *supportedp = 271 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); 272 break; 273 case EFX_MCDI_FEATURE_LINK_CONTROL: 274 /* 275 * Admin privilege used prior to introduction of 276 * specific flag. 277 */ 278 *supportedp = 279 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, LINK) || 280 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); 281 break; 282 case EFX_MCDI_FEATURE_MACADDR_CHANGE: 283 /* 284 * Admin privilege must be used prior to introduction of 285 * mac spoofing privilege (at v4.6), which is used up to 286 * introduction of change mac spoofing privilege (at v4.7) 287 */ 288 *supportedp = 289 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, CHANGE_MAC) || 290 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) || 291 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); 292 break; 293 case EFX_MCDI_FEATURE_MAC_SPOOFING: 294 /* 295 * Admin privilege must be used prior to introduction of 296 * mac spoofing privilege (at v4.6), which is used up to 297 * introduction of mac spoofing TX privilege (at v4.7) 298 */ 299 *supportedp = 300 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING_TX) || 301 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) || 302 EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); 303 break; 304 default: 305 rc = ENOTSUP; 306 goto fail1; 307 } 308 309 return (0); 310 311 fail1: 312 EFSYS_PROBE1(fail1, efx_rc_t, rc); 313 314 return (rc); 315 } 316 317 #endif /* EFSYS_OPT_MCDI */ 318 319 #endif /* EFX_OPTS_EF10() */ 320