aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc
diff options
context:
space:
mode:
authorSteve Hodgson <shodgson@solarflare.com>2010-02-03 04:30:17 -0500
committerDavid S. Miller <davem@davemloft.net>2010-02-03 22:12:43 -0500
commit8b2103add08b79abd3ac7015b4bac744c0af534e (patch)
tree018ee0b97a17a702dd163d17cf13483ae1906408 /drivers/net/sfc
parent5297a98d5dd6de86fe1e2ffc9ea60cdf59b71443 (diff)
sfc: Handle firmware assertion failure while resetting
This allows the driver to recover if the MC firmware has crashed due to an assertion failure. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r--drivers/net/sfc/mcdi.c63
-rw-r--r--drivers/net/sfc/siena.c6
2 files changed, 44 insertions, 25 deletions
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c
index 9f035b9f0350..e9f0e5ee4519 100644
--- a/drivers/net/sfc/mcdi.c
+++ b/drivers/net/sfc/mcdi.c
@@ -896,29 +896,27 @@ fail:
896 return rc; 896 return rc;
897} 897}
898 898
899int efx_mcdi_handle_assertion(struct efx_nic *efx) 899static int efx_mcdi_read_assertion(struct efx_nic *efx)
900{ 900{
901 union { 901 u8 inbuf[MC_CMD_GET_ASSERTS_IN_LEN];
902 u8 asserts[MC_CMD_GET_ASSERTS_IN_LEN]; 902 u8 outbuf[MC_CMD_GET_ASSERTS_OUT_LEN];
903 u8 reboot[MC_CMD_REBOOT_IN_LEN];
904 } inbuf;
905 u8 assertion[MC_CMD_GET_ASSERTS_OUT_LEN];
906 unsigned int flags, index, ofst; 903 unsigned int flags, index, ofst;
907 const char *reason; 904 const char *reason;
908 size_t outlen; 905 size_t outlen;
909 int retry; 906 int retry;
910 int rc; 907 int rc;
911 908
912 /* Check if the MC is in the assertion handler, retrying twice. Once 909 /* Attempt to read any stored assertion state before we reboot
910 * the mcfw out of the assertion handler. Retry twice, once
913 * because a boot-time assertion might cause this command to fail 911 * because a boot-time assertion might cause this command to fail
914 * with EINTR. And once again because GET_ASSERTS can race with 912 * with EINTR. And once again because GET_ASSERTS can race with
915 * MC_CMD_REBOOT running on the other port. */ 913 * MC_CMD_REBOOT running on the other port. */
916 retry = 2; 914 retry = 2;
917 do { 915 do {
918 MCDI_SET_DWORD(inbuf.asserts, GET_ASSERTS_IN_CLEAR, 0); 916 MCDI_SET_DWORD(inbuf, GET_ASSERTS_IN_CLEAR, 1);
919 rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, 917 rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS,
920 inbuf.asserts, MC_CMD_GET_ASSERTS_IN_LEN, 918 inbuf, MC_CMD_GET_ASSERTS_IN_LEN,
921 assertion, sizeof(assertion), &outlen); 919 outbuf, sizeof(outbuf), &outlen);
922 } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); 920 } while ((rc == -EINTR || rc == -EIO) && retry-- > 0);
923 921
924 if (rc) 922 if (rc)
@@ -926,21 +924,11 @@ int efx_mcdi_handle_assertion(struct efx_nic *efx)
926 if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) 924 if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN)
927 return -EINVAL; 925 return -EINVAL;
928 926
929 flags = MCDI_DWORD(assertion, GET_ASSERTS_OUT_GLOBAL_FLAGS); 927 /* Print out any recorded assertion state */
928 flags = MCDI_DWORD(outbuf, GET_ASSERTS_OUT_GLOBAL_FLAGS);
930 if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) 929 if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
931 return 0; 930 return 0;
932 931
933 /* Reset the hardware atomically such that only one port with succeed.
934 * This command will succeed if a reboot is no longer required (because
935 * the other port did it first), but fail with EIO if it succeeds.
936 */
937 BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
938 MCDI_SET_DWORD(inbuf.reboot, REBOOT_IN_FLAGS,
939 MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION);
940 efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf.reboot, MC_CMD_REBOOT_IN_LEN,
941 NULL, 0, NULL);
942
943 /* Print out the assertion */
944 reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) 932 reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
945 ? "system-level assertion" 933 ? "system-level assertion"
946 : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) 934 : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
@@ -949,20 +937,45 @@ int efx_mcdi_handle_assertion(struct efx_nic *efx)
949 ? "watchdog reset" 937 ? "watchdog reset"
950 : "unknown assertion"; 938 : "unknown assertion";
951 EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, 939 EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason,
952 MCDI_DWORD(assertion, GET_ASSERTS_OUT_SAVED_PC_OFFS), 940 MCDI_DWORD(outbuf, GET_ASSERTS_OUT_SAVED_PC_OFFS),
953 MCDI_DWORD(assertion, GET_ASSERTS_OUT_THREAD_OFFS)); 941 MCDI_DWORD(outbuf, GET_ASSERTS_OUT_THREAD_OFFS));
954 942
955 /* Print out the registers */ 943 /* Print out the registers */
956 ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; 944 ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
957 for (index = 1; index < 32; index++) { 945 for (index = 1; index < 32; index++) {
958 EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, 946 EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index,
959 MCDI_DWORD2(assertion, ofst)); 947 MCDI_DWORD2(outbuf, ofst));
960 ofst += sizeof(efx_dword_t); 948 ofst += sizeof(efx_dword_t);
961 } 949 }
962 950
963 return 0; 951 return 0;
964} 952}
965 953
954static void efx_mcdi_exit_assertion(struct efx_nic *efx)
955{
956 u8 inbuf[MC_CMD_REBOOT_IN_LEN];
957
958 /* Atomically reboot the mcfw out of the assertion handler */
959 BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
960 MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS,
961 MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION);
962 efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, MC_CMD_REBOOT_IN_LEN,
963 NULL, 0, NULL);
964}
965
966int efx_mcdi_handle_assertion(struct efx_nic *efx)
967{
968 int rc;
969
970 rc = efx_mcdi_read_assertion(efx);
971 if (rc)
972 return rc;
973
974 efx_mcdi_exit_assertion(efx);
975
976 return 0;
977}
978
966void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) 979void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
967{ 980{
968 u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; 981 u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN];
diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c
index f8c6771e66d8..0e4c13abf087 100644
--- a/drivers/net/sfc/siena.c
+++ b/drivers/net/sfc/siena.c
@@ -181,6 +181,12 @@ static int siena_test_registers(struct efx_nic *efx)
181 181
182static int siena_reset_hw(struct efx_nic *efx, enum reset_type method) 182static int siena_reset_hw(struct efx_nic *efx, enum reset_type method)
183{ 183{
184 int rc;
185
186 /* Recover from a failed assertion pre-reset */
187 rc = efx_mcdi_handle_assertion(efx);
188 if (rc)
189 return rc;
184 190
185 if (method == RESET_TYPE_WORLD) 191 if (method == RESET_TYPE_WORLD)
186 return efx_mcdi_reset_mc(efx); 192 return efx_mcdi_reset_mc(efx);