if (!idev->fw_status_ready)
                return -ENXIO;
 
-       /* wait at least one watchdog period since the last heartbeat */
+       /* Because of some variability in the actual FW heartbeat, we
+        * wait longer than the DEVCMD_TIMEOUT before checking again.
+        */
        last_check_time = idev->last_hb_time;
-       if (time_before(check_time, last_check_time + ionic->watchdog_period))
+       if (time_before(check_time, last_check_time + DEVCMD_TIMEOUT * 2 * HZ))
                return 0;
 
        fw_hb = ioread32(&idev->dev_info_regs->fw_heartbeat);
 
                if (remaining)
                        break;
 
-               /* interrupt the wait if FW stopped */
+               /* force a check of FW status and break out if FW reset */
+               (void)ionic_heartbeat_check(lif->ionic);
                if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
                     !lif->ionic->idev.fw_status_ready) ||
                    test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
                        if (do_msg)
-                               netdev_err(netdev, "%s (%d) interrupted, FW in reset\n",
-                                          name, ctx->cmd.cmd.opcode);
+                               netdev_warn(netdev, "%s (%d) interrupted, FW in reset\n",
+                                           name, ctx->cmd.cmd.opcode);
                        ctx->comp.comp.status = IONIC_RC_ERROR;
                        return -ENXIO;
                }
        unsigned long start_time;
        unsigned long max_wait;
        unsigned long duration;
+       int done = 0;
+       bool fw_up;
        int opcode;
-       int hb = 0;
-       int done;
        int err;
 
        /* Wait for dev cmd to complete, retrying if we get EAGAIN,
 try_again:
        opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode);
        start_time = jiffies;
-       do {
+       for (fw_up = ionic_is_fw_running(idev);
+            !done && fw_up && time_before(jiffies, max_wait);
+            fw_up = ionic_is_fw_running(idev)) {
                done = ionic_dev_cmd_done(idev);
                if (done)
                        break;
                usleep_range(100, 200);
-
-               /* Don't check the heartbeat on FW_CONTROL commands as they are
-                * notorious for interrupting the firmware's heartbeat update.
-                */
-               if (opcode != IONIC_CMD_FW_CONTROL)
-                       hb = ionic_heartbeat_check(ionic);
-       } while (!done && !hb && time_before(jiffies, max_wait));
+       }
        duration = jiffies - start_time;
 
        dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
                ionic_opcode_to_str(opcode), opcode,
                done, duration / HZ, duration);
 
-       if (!done && hb) {
-               /* It is possible (but unlikely) that FW was busy and missed a
-                * heartbeat check but is still alive and will process this
-                * request, so don't clean the dev_cmd in this case.
-                */
-               dev_dbg(ionic->dev, "DEVCMD %s (%d) failed - FW halted\n",
-                       ionic_opcode_to_str(opcode), opcode);
+       if (!done && !fw_up) {
+               ionic_dev_cmd_clean(ionic);
+               dev_warn(ionic->dev, "DEVCMD %s (%d) interrupted - FW is down\n",
+                        ionic_opcode_to_str(opcode), opcode);
                return -ENXIO;
        }