diff options
Diffstat (limited to 'drivers/net/sfc/mcdi.c')
-rw-r--r-- | drivers/net/sfc/mcdi.c | 109 |
1 files changed, 84 insertions, 25 deletions
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c index 9f035b9f0350..86610db2cff5 100644 --- a/drivers/net/sfc/mcdi.c +++ b/drivers/net/sfc/mcdi.c | |||
@@ -896,29 +896,73 @@ fail: | |||
896 | return rc; | 896 | return rc; |
897 | } | 897 | } |
898 | 898 | ||
899 | int efx_mcdi_handle_assertion(struct efx_nic *efx) | 899 | static int efx_mcdi_nvram_test(struct efx_nic *efx, unsigned int type) |
900 | { | ||
901 | u8 inbuf[MC_CMD_NVRAM_TEST_IN_LEN]; | ||
902 | u8 outbuf[MC_CMD_NVRAM_TEST_OUT_LEN]; | ||
903 | int rc; | ||
904 | |||
905 | MCDI_SET_DWORD(inbuf, NVRAM_TEST_IN_TYPE, type); | ||
906 | |||
907 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TEST, inbuf, sizeof(inbuf), | ||
908 | outbuf, sizeof(outbuf), NULL); | ||
909 | if (rc) | ||
910 | return rc; | ||
911 | |||
912 | switch (MCDI_DWORD(outbuf, NVRAM_TEST_OUT_RESULT)) { | ||
913 | case MC_CMD_NVRAM_TEST_PASS: | ||
914 | case MC_CMD_NVRAM_TEST_NOTSUPP: | ||
915 | return 0; | ||
916 | default: | ||
917 | return -EIO; | ||
918 | } | ||
919 | } | ||
920 | |||
921 | int efx_mcdi_nvram_test_all(struct efx_nic *efx) | ||
922 | { | ||
923 | u32 nvram_types; | ||
924 | unsigned int type; | ||
925 | int rc; | ||
926 | |||
927 | rc = efx_mcdi_nvram_types(efx, &nvram_types); | ||
928 | if (rc) | ||
929 | return rc; | ||
930 | |||
931 | type = 0; | ||
932 | while (nvram_types != 0) { | ||
933 | if (nvram_types & 1) { | ||
934 | rc = efx_mcdi_nvram_test(efx, type); | ||
935 | if (rc) | ||
936 | return rc; | ||
937 | } | ||
938 | type++; | ||
939 | nvram_types >>= 1; | ||
940 | } | ||
941 | |||
942 | return 0; | ||
943 | } | ||
944 | |||
945 | static int efx_mcdi_read_assertion(struct efx_nic *efx) | ||
900 | { | 946 | { |
901 | union { | 947 | u8 inbuf[MC_CMD_GET_ASSERTS_IN_LEN]; |
902 | u8 asserts[MC_CMD_GET_ASSERTS_IN_LEN]; | 948 | 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; | 949 | unsigned int flags, index, ofst; |
907 | const char *reason; | 950 | const char *reason; |
908 | size_t outlen; | 951 | size_t outlen; |
909 | int retry; | 952 | int retry; |
910 | int rc; | 953 | int rc; |
911 | 954 | ||
912 | /* Check if the MC is in the assertion handler, retrying twice. Once | 955 | /* Attempt to read any stored assertion state before we reboot |
956 | * the mcfw out of the assertion handler. Retry twice, once | ||
913 | * because a boot-time assertion might cause this command to fail | 957 | * because a boot-time assertion might cause this command to fail |
914 | * with EINTR. And once again because GET_ASSERTS can race with | 958 | * with EINTR. And once again because GET_ASSERTS can race with |
915 | * MC_CMD_REBOOT running on the other port. */ | 959 | * MC_CMD_REBOOT running on the other port. */ |
916 | retry = 2; | 960 | retry = 2; |
917 | do { | 961 | do { |
918 | MCDI_SET_DWORD(inbuf.asserts, GET_ASSERTS_IN_CLEAR, 0); | 962 | MCDI_SET_DWORD(inbuf, GET_ASSERTS_IN_CLEAR, 1); |
919 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, | 963 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, |
920 | inbuf.asserts, MC_CMD_GET_ASSERTS_IN_LEN, | 964 | inbuf, MC_CMD_GET_ASSERTS_IN_LEN, |
921 | assertion, sizeof(assertion), &outlen); | 965 | outbuf, sizeof(outbuf), &outlen); |
922 | } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); | 966 | } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); |
923 | 967 | ||
924 | if (rc) | 968 | if (rc) |
@@ -926,21 +970,11 @@ int efx_mcdi_handle_assertion(struct efx_nic *efx) | |||
926 | if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) | 970 | if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) |
927 | return -EINVAL; | 971 | return -EINVAL; |
928 | 972 | ||
929 | flags = MCDI_DWORD(assertion, GET_ASSERTS_OUT_GLOBAL_FLAGS); | 973 | /* Print out any recorded assertion state */ |
974 | flags = MCDI_DWORD(outbuf, GET_ASSERTS_OUT_GLOBAL_FLAGS); | ||
930 | if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) | 975 | if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) |
931 | return 0; | 976 | return 0; |
932 | 977 | ||
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) | 978 | reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) |
945 | ? "system-level assertion" | 979 | ? "system-level assertion" |
946 | : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) | 980 | : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) |
@@ -949,20 +983,45 @@ int efx_mcdi_handle_assertion(struct efx_nic *efx) | |||
949 | ? "watchdog reset" | 983 | ? "watchdog reset" |
950 | : "unknown assertion"; | 984 | : "unknown assertion"; |
951 | EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, | 985 | 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), | 986 | MCDI_DWORD(outbuf, GET_ASSERTS_OUT_SAVED_PC_OFFS), |
953 | MCDI_DWORD(assertion, GET_ASSERTS_OUT_THREAD_OFFS)); | 987 | MCDI_DWORD(outbuf, GET_ASSERTS_OUT_THREAD_OFFS)); |
954 | 988 | ||
955 | /* Print out the registers */ | 989 | /* Print out the registers */ |
956 | ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; | 990 | ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; |
957 | for (index = 1; index < 32; index++) { | 991 | for (index = 1; index < 32; index++) { |
958 | EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, | 992 | EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, |
959 | MCDI_DWORD2(assertion, ofst)); | 993 | MCDI_DWORD2(outbuf, ofst)); |
960 | ofst += sizeof(efx_dword_t); | 994 | ofst += sizeof(efx_dword_t); |
961 | } | 995 | } |
962 | 996 | ||
963 | return 0; | 997 | return 0; |
964 | } | 998 | } |
965 | 999 | ||
1000 | static void efx_mcdi_exit_assertion(struct efx_nic *efx) | ||
1001 | { | ||
1002 | u8 inbuf[MC_CMD_REBOOT_IN_LEN]; | ||
1003 | |||
1004 | /* Atomically reboot the mcfw out of the assertion handler */ | ||
1005 | BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); | ||
1006 | MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, | ||
1007 | MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); | ||
1008 | efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, MC_CMD_REBOOT_IN_LEN, | ||
1009 | NULL, 0, NULL); | ||
1010 | } | ||
1011 | |||
1012 | int efx_mcdi_handle_assertion(struct efx_nic *efx) | ||
1013 | { | ||
1014 | int rc; | ||
1015 | |||
1016 | rc = efx_mcdi_read_assertion(efx); | ||
1017 | if (rc) | ||
1018 | return rc; | ||
1019 | |||
1020 | efx_mcdi_exit_assertion(efx); | ||
1021 | |||
1022 | return 0; | ||
1023 | } | ||
1024 | |||
966 | void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) | 1025 | void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) |
967 | { | 1026 | { |
968 | u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; | 1027 | u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; |