aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/sfc/mcdi.c
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2011-12-20 18:39:31 -0500
committerBen Hutchings <bhutchings@solarflare.com>2012-01-26 19:10:44 -0500
commit3f713bf4dd9f7ef2a2c5e9ec124b3d992a2669db (patch)
tree4588c2c536c165e94a3a650260453803a9a7b747 /drivers/net/ethernet/sfc/mcdi.c
parente332bcb3d149644c344ee5fdf1f6e4ac39e9d9a3 (diff)
sfc: Make handling of MC reboot more reliable
When the MC reboots, either as part of a firmware upgrade or due to a bug, it attempts to complete (with an error) any requests that were outstanding before the reboot. Since there is an inherent race condition in checking this, it will also write to a status word in shared memory. If we look at each of these separately, we may detect each reboot twice, resulting in a spurious command failure after a firmware upgrade or frustrating recovery from a firmware bug. Instead, if a request completion indicates a reboot, we must poll and clear the status word. This bug was previously masked by use of an incorrect address for the status word. Fix that, using the definition now included in mcdi_pcol.h. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/ethernet/sfc/mcdi.c')
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c33
1 files changed, 27 insertions, 6 deletions
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index 7c405d16692e..f16145d92817 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -27,8 +27,6 @@
27#define CMD_NOTIFY_PORT1 4 27#define CMD_NOTIFY_PORT1 4
28#define CMD_PDU_PORT0 0x008 28#define CMD_PDU_PORT0 0x008
29#define CMD_PDU_PORT1 0x108 29#define CMD_PDU_PORT1 0x108
30#define REBOOT_FLAG_PORT0 0x3f8
31#define REBOOT_FLAG_PORT1 0x3fc
32 30
33#define MCDI_RPC_TIMEOUT 10 /*seconds */ 31#define MCDI_RPC_TIMEOUT 10 /*seconds */
34 32
@@ -36,8 +34,16 @@
36 (efx_port_num(efx) ? CMD_PDU_PORT1 : CMD_PDU_PORT0) 34 (efx_port_num(efx) ? CMD_PDU_PORT1 : CMD_PDU_PORT0)
37#define MCDI_DOORBELL(efx) \ 35#define MCDI_DOORBELL(efx) \
38 (efx_port_num(efx) ? CMD_NOTIFY_PORT1 : CMD_NOTIFY_PORT0) 36 (efx_port_num(efx) ? CMD_NOTIFY_PORT1 : CMD_NOTIFY_PORT0)
39#define MCDI_REBOOT_FLAG(efx) \ 37#define MCDI_STATUS(efx) \
40 (efx_port_num(efx) ? REBOOT_FLAG_PORT1 : REBOOT_FLAG_PORT0) 38 (efx_port_num(efx) ? MC_SMEM_P1_STATUS_OFST : MC_SMEM_P0_STATUS_OFST)
39
40/* A reboot/assertion causes the MCDI status word to be set after the
41 * command word is set or a REBOOT event is sent. If we notice a reboot
42 * via these mechanisms then wait 10ms for the status word to be set. */
43#define MCDI_STATUS_DELAY_US 100
44#define MCDI_STATUS_DELAY_COUNT 100
45#define MCDI_STATUS_SLEEP_MS \
46 (MCDI_STATUS_DELAY_US * MCDI_STATUS_DELAY_COUNT / 1000)
41 47
42#define SEQ_MASK \ 48#define SEQ_MASK \
43 EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ)) 49 EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ))
@@ -210,7 +216,7 @@ out:
210/* Test and clear MC-rebooted flag for this port/function */ 216/* Test and clear MC-rebooted flag for this port/function */
211int efx_mcdi_poll_reboot(struct efx_nic *efx) 217int efx_mcdi_poll_reboot(struct efx_nic *efx)
212{ 218{
213 unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx); 219 unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_STATUS(efx);
214 efx_dword_t reg; 220 efx_dword_t reg;
215 uint32_t value; 221 uint32_t value;
216 222
@@ -384,6 +390,11 @@ int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
384 netif_dbg(efx, hw, efx->net_dev, 390 netif_dbg(efx, hw, efx->net_dev,
385 "MC command 0x%x inlen %d failed rc=%d\n", 391 "MC command 0x%x inlen %d failed rc=%d\n",
386 cmd, (int)inlen, -rc); 392 cmd, (int)inlen, -rc);
393
394 if (rc == -EIO || rc == -EINTR) {
395 msleep(MCDI_STATUS_SLEEP_MS);
396 efx_mcdi_poll_reboot(efx);
397 }
387 } 398 }
388 399
389 efx_mcdi_release(mcdi); 400 efx_mcdi_release(mcdi);
@@ -465,10 +476,20 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
465 mcdi->resplen = 0; 476 mcdi->resplen = 0;
466 ++mcdi->credits; 477 ++mcdi->credits;
467 } 478 }
468 } else 479 } else {
480 int count;
481
469 /* Nobody was waiting for an MCDI request, so trigger a reset */ 482 /* Nobody was waiting for an MCDI request, so trigger a reset */
470 efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); 483 efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
471 484
485 /* Consume the status word since efx_mcdi_rpc_finish() won't */
486 for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) {
487 if (efx_mcdi_poll_reboot(efx))
488 break;
489 udelay(MCDI_STATUS_DELAY_US);
490 }
491 }
492
472 spin_unlock(&mcdi->iface_lock); 493 spin_unlock(&mcdi->iface_lock);
473} 494}
474 495