return rc;
 }
 
+#define EFX_MCDI_NVRAM_DEFAULT_WRITE_LEN 128
+
 int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
                        size_t *size_out, size_t *erase_size_out,
-                       bool *protected_out)
+                       size_t *write_size_out, bool *protected_out)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_INFO_IN_LEN);
-       MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_OUT_LEN);
+       MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_V2_OUT_LEN);
+       size_t write_size = 0;
        size_t outlen;
        int rc;
 
                goto fail;
        }
 
+       if (outlen >= MC_CMD_NVRAM_INFO_V2_OUT_LEN)
+               write_size = MCDI_DWORD(outbuf, NVRAM_INFO_V2_OUT_WRITESIZE);
+       else
+               write_size = EFX_MCDI_NVRAM_DEFAULT_WRITE_LEN;
+
+       *write_size_out = write_size;
        *size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE);
        *erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE);
        *protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) &
        return rc;
 }
 
-#ifdef CONFIG_SFC_MTD
-
 #define EFX_MCDI_NVRAM_LEN_MAX 128
 
-static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
+int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN);
        int rc;
        return rc;
 }
 
+#ifdef CONFIG_SFC_MTD
+
 static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
                               loff_t offset, u8 *buffer, size_t length)
 {
        return 0;
 }
 
-static int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
-                               loff_t offset, const u8 *buffer, size_t length)
+#endif /* CONFIG_SFC_MTD */
+
+int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
+                        loff_t offset, const u8 *buffer, size_t length)
 {
-       MCDI_DECLARE_BUF(inbuf,
-                        MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
+       efx_dword_t *inbuf;
+       size_t inlen;
        int rc;
 
+       inlen = ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4);
+       inbuf = kzalloc(inlen, GFP_KERNEL);
+       if (!inbuf)
+               return -ENOMEM;
+
        MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
        MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
        MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
 
        BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
 
-       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
-                         ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
-                         NULL, 0, NULL);
+       rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, inlen, NULL, 0, NULL);
+       kfree(inbuf);
+
        return rc;
 }
 
-static int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
-                               loff_t offset, size_t length)
+int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, loff_t offset,
+                        size_t length)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
        int rc;
        return rc;
 }
 
-static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
+int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type,
+                                enum efx_update_finish_mode mode)
 {
        MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN);
        MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN);
        int rc, rc2;
 
        MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
-       /* Always set this flag. Old firmware ignores it */
-       MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
+
+       /* Old firmware doesn't support background update finish and abort
+        * operations. Fallback to waiting if the requested mode is not
+        * supported.
+        */
+       if (!efx_has_cap(efx, NVRAM_UPDATE_POLL_VERIFY_RESULT) ||
+           (!efx_has_cap(efx, NVRAM_UPDATE_ABORT_SUPPORTED) &&
+            mode == EFX_UPDATE_FINISH_ABORT))
+               mode = EFX_UPDATE_FINISH_WAIT;
+
+       MCDI_POPULATE_DWORD_4(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
                              NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT,
-                             1);
+                             (mode != EFX_UPDATE_FINISH_ABORT),
+                             NVRAM_UPDATE_FINISH_V2_IN_FLAG_RUN_IN_BACKGROUND,
+                             (mode == EFX_UPDATE_FINISH_BACKGROUND),
+                             NVRAM_UPDATE_FINISH_V2_IN_FLAG_POLL_VERIFY_RESULT,
+                             (mode == EFX_UPDATE_FINISH_POLL),
+                             NVRAM_UPDATE_FINISH_V2_IN_FLAG_ABORT,
+                             (mode == EFX_UPDATE_FINISH_ABORT));
 
        rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
                          outbuf, sizeof(outbuf), &outlen);
        if (!rc && outlen >= MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) {
                rc2 = MCDI_DWORD(outbuf, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE);
-               if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS)
+               if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS &&
+                   rc2 != MC_CMD_NVRAM_VERIFY_RC_PENDING)
                        netif_err(efx, drv, efx->net_dev,
                                  "NVRAM update failed verification with code 0x%x\n",
                                  rc2);
                switch (rc2) {
                case MC_CMD_NVRAM_VERIFY_RC_SUCCESS:
                        break;
+               case MC_CMD_NVRAM_VERIFY_RC_PENDING:
+                       rc = -EAGAIN;
+                       break;
                case MC_CMD_NVRAM_VERIFY_RC_CMS_CHECK_FAILED:
                case MC_CMD_NVRAM_VERIFY_RC_MESSAGE_DIGEST_CHECK_FAILED:
                case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHECK_FAILED:
                case MC_CMD_NVRAM_VERIFY_RC_NO_VALID_SIGNATURES:
                case MC_CMD_NVRAM_VERIFY_RC_NO_TRUSTED_APPROVERS:
                case MC_CMD_NVRAM_VERIFY_RC_NO_SIGNATURE_MATCH:
+               case MC_CMD_NVRAM_VERIFY_RC_REJECT_TEST_SIGNED:
+               case MC_CMD_NVRAM_VERIFY_RC_SECURITY_LEVEL_DOWNGRADE:
                        rc = -EPERM;
                        break;
                default:
        return rc;
 }
 
+#define        EFX_MCDI_NVRAM_UPDATE_FINISH_INITIAL_POLL_DELAY_MS 5
+#define        EFX_MCDI_NVRAM_UPDATE_FINISH_MAX_POLL_DELAY_MS 5000
+#define        EFX_MCDI_NVRAM_UPDATE_FINISH_RETRIES 185
+
+int efx_mcdi_nvram_update_finish_polled(struct efx_nic *efx, unsigned int type)
+{
+       unsigned int delay = EFX_MCDI_NVRAM_UPDATE_FINISH_INITIAL_POLL_DELAY_MS;
+       unsigned int retry = 0;
+       int rc;
+
+       /* NVRAM updates can take a long time (e.g. up to 1 minute for bundle
+        * images). Polling for NVRAM update completion ensures that other MCDI
+        * commands can be issued before the background NVRAM update completes.
+        *
+        * The initial call either completes the update synchronously, or
+        * returns -EAGAIN to indicate processing is continuing. In the latter
+        * case, we poll for at least 900 seconds, at increasing intervals
+        * (5ms, 50ms, 500ms, 5s).
+        */
+       rc = efx_mcdi_nvram_update_finish(efx, type, EFX_UPDATE_FINISH_BACKGROUND);
+       while (rc == -EAGAIN) {
+               if (retry > EFX_MCDI_NVRAM_UPDATE_FINISH_RETRIES)
+                       return -ETIMEDOUT;
+               retry++;
+
+               msleep(delay);
+               if (delay < EFX_MCDI_NVRAM_UPDATE_FINISH_MAX_POLL_DELAY_MS)
+                       delay *= 10;
+
+               rc = efx_mcdi_nvram_update_finish(efx, type, EFX_UPDATE_FINISH_POLL);
+       }
+       return rc;
+}
+
+#ifdef CONFIG_SFC_MTD
+
 int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
                      size_t len, size_t *retlen, u8 *buffer)
 {
 
        if (part->updating) {
                part->updating = false;
-               rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
+               rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type,
+                                                 EFX_UPDATE_FINISH_WAIT);
        }
 
        return rc;
 
 int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out);
 int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
                        size_t *size_out, size_t *erase_size_out,
-                       bool *protected_out);
+                       size_t *write_size_out, bool *protected_out);
 int efx_new_mcdi_nvram_test_all(struct efx_nic *efx);
 int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
                            u32 *subtype, u16 version[4], char *desc,
 static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {}
 #endif
 
+int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type);
+int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
+                        loff_t offset, const u8 *buffer, size_t length);
+int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
+                        loff_t offset, size_t length);
+int efx_mcdi_nvram_metadata(struct efx_nic *efx, unsigned int type,
+                           u32 *subtype, u16 version[4], char *desc,
+                           size_t descsize);
+
+enum efx_update_finish_mode {
+       EFX_UPDATE_FINISH_WAIT,
+       EFX_UPDATE_FINISH_BACKGROUND,
+       EFX_UPDATE_FINISH_POLL,
+       EFX_UPDATE_FINISH_ABORT,
+};
+
+int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type,
+                                enum efx_update_finish_mode mode);
+int efx_mcdi_nvram_update_finish_polled(struct efx_nic *efx, unsigned int type);
+
 #ifdef CONFIG_SFC_MTD
 int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
                      size_t *retlen, u8 *buffer);