struct workqueue_struct *wq;
        struct work_struct fw_live_patch_work;
        struct work_struct reset_request_work;
+       struct work_struct reset_unload_work;
        struct work_struct reset_reload_work;
        struct work_struct reset_now_work;
        struct work_struct reset_abort_work;
        int ret;
 };
 
+enum {
+       MLX5_FW_RST_STATE_IDLE = 0,
+       MLX5_FW_RST_STATE_TOGGLE_REQ = 4,
+};
+
+enum {
+       MLX5_RST_STATE_BIT_NUM = 12,
+       MLX5_RST_ACK_BIT_NUM = 22,
+};
+
+static u8 mlx5_get_fw_rst_state(struct mlx5_core_dev *dev)
+{
+       return (ioread32be(&dev->iseg->initializing) >> MLX5_RST_STATE_BIT_NUM) & 0xF;
+}
+
+static void mlx5_set_fw_rst_ack(struct mlx5_core_dev *dev)
+{
+       iowrite32be(BIT(MLX5_RST_ACK_BIT_NUM), &dev->iseg->initializing);
+}
+
 static int mlx5_fw_reset_enable_remote_dev_reset_set(struct devlink *devlink, u32 id,
                                                     struct devlink_param_gset_ctx *ctx)
 {
        return mlx5_reg_mfrl_set(dev, MLX5_MFRL_REG_RESET_LEVEL0, 0, 0, false);
 }
 
-static void mlx5_fw_reset_complete_reload(struct mlx5_core_dev *dev)
+static void mlx5_fw_reset_complete_reload(struct mlx5_core_dev *dev, bool unloaded)
 {
        struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset;
 
        if (test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags)) {
                complete(&fw_reset->done);
        } else {
-               mlx5_unload_one(dev, false);
+               if (!unloaded)
+                       mlx5_unload_one(dev, false);
                if (mlx5_health_wait_pci_up(dev))
                        mlx5_core_err(dev, "reset reload flow aborted, PCI reads still not working\n");
                else
 
        mlx5_sync_reset_clear_reset_requested(dev, false);
        mlx5_enter_error_state(dev, true);
-       mlx5_fw_reset_complete_reload(dev);
+       mlx5_fw_reset_complete_reload(dev, false);
 }
 
 #define MLX5_RESET_POLL_INTERVAL       (HZ / 10)
        mlx5_enter_error_state(dev, true);
 done:
        fw_reset->ret = err;
-       mlx5_fw_reset_complete_reload(dev);
+       mlx5_fw_reset_complete_reload(dev, false);
+}
+
+static void mlx5_sync_reset_unload_event(struct work_struct *work)
+{
+       struct mlx5_fw_reset *fw_reset;
+       struct mlx5_core_dev *dev;
+       unsigned long timeout;
+       bool reset_action;
+       u8 rst_state;
+       int err;
+
+       fw_reset = container_of(work, struct mlx5_fw_reset, reset_unload_work);
+       dev = fw_reset->dev;
+
+       if (mlx5_sync_reset_clear_reset_requested(dev, false))
+               return;
+
+       mlx5_core_warn(dev, "Sync Reset Unload. Function is forced down.\n");
+
+       err = mlx5_cmd_fast_teardown_hca(dev);
+       if (err)
+               mlx5_core_warn(dev, "Fast teardown failed, unloading, err %d\n", err);
+       else
+               mlx5_enter_error_state(dev, true);
+
+       if (test_bit(MLX5_FW_RESET_FLAGS_PENDING_COMP, &fw_reset->reset_flags))
+               mlx5_unload_one_devl_locked(dev, false);
+       else
+               mlx5_unload_one(dev, false);
+
+       mlx5_set_fw_rst_ack(dev);
+       mlx5_core_warn(dev, "Sync Reset Unload done, device reset expected\n");
+
+       reset_action = false;
+       timeout = jiffies + msecs_to_jiffies(mlx5_tout_ms(dev, RESET_UNLOAD));
+       do {
+               rst_state = mlx5_get_fw_rst_state(dev);
+               if (rst_state == MLX5_FW_RST_STATE_TOGGLE_REQ ||
+                   rst_state == MLX5_FW_RST_STATE_IDLE) {
+                       reset_action = true;
+                       break;
+               }
+               msleep(20);
+       } while (!time_after(jiffies, timeout));
+
+       if (!reset_action) {
+               mlx5_core_err(dev, "Got timeout waiting for sync reset action, state = %u\n",
+                             rst_state);
+               fw_reset->ret = -ETIMEDOUT;
+               goto done;
+       }
+
+       mlx5_core_warn(dev, "Sync Reset, got reset action. rst_state = %u\n", rst_state);
+       if (rst_state == MLX5_FW_RST_STATE_TOGGLE_REQ) {
+               err = mlx5_pci_link_toggle(dev);
+               if (err) {
+                       mlx5_core_warn(dev, "mlx5_pci_link_toggle failed, err %d\n", err);
+                       fw_reset->ret = err;
+               }
+       }
+
+done:
+       mlx5_fw_reset_complete_reload(dev, true);
 }
 
 static void mlx5_sync_reset_abort_event(struct work_struct *work)
        case MLX5_SYNC_RST_STATE_RESET_REQUEST:
                queue_work(fw_reset->wq, &fw_reset->reset_request_work);
                break;
+       case MLX5_SYNC_RST_STATE_RESET_UNLOAD:
+               queue_work(fw_reset->wq, &fw_reset->reset_unload_work);
+               break;
        case MLX5_SYNC_RST_STATE_RESET_NOW:
                queue_work(fw_reset->wq, &fw_reset->reset_now_work);
                break;
 int mlx5_fw_reset_wait_reset_done(struct mlx5_core_dev *dev)
 {
        unsigned long pci_sync_update_timeout = mlx5_tout_ms(dev, PCI_SYNC_UPDATE);
-       unsigned long timeout = msecs_to_jiffies(pci_sync_update_timeout);
        struct mlx5_fw_reset *fw_reset = dev->priv.fw_reset;
+       unsigned long timeout;
        int err;
 
+       if (MLX5_CAP_GEN(dev, pci_sync_for_fw_update_with_driver_unload))
+               pci_sync_update_timeout += mlx5_tout_ms(dev, RESET_UNLOAD);
+       timeout = msecs_to_jiffies(pci_sync_update_timeout);
        if (!wait_for_completion_timeout(&fw_reset->done, timeout)) {
                mlx5_core_warn(dev, "FW sync reset timeout after %lu seconds\n",
                               pci_sync_update_timeout / 1000);
        set_bit(MLX5_FW_RESET_FLAGS_DROP_NEW_REQUESTS, &fw_reset->reset_flags);
        cancel_work_sync(&fw_reset->fw_live_patch_work);
        cancel_work_sync(&fw_reset->reset_request_work);
+       cancel_work_sync(&fw_reset->reset_unload_work);
        cancel_work_sync(&fw_reset->reset_reload_work);
        cancel_work_sync(&fw_reset->reset_now_work);
        cancel_work_sync(&fw_reset->reset_abort_work);
 
        INIT_WORK(&fw_reset->fw_live_patch_work, mlx5_fw_live_patch_event);
        INIT_WORK(&fw_reset->reset_request_work, mlx5_sync_reset_request_event);
+       INIT_WORK(&fw_reset->reset_unload_work, mlx5_sync_reset_unload_event);
        INIT_WORK(&fw_reset->reset_reload_work, mlx5_sync_reset_reload_work);
        INIT_WORK(&fw_reset->reset_now_work, mlx5_sync_reset_now_event);
        INIT_WORK(&fw_reset->reset_abort_work, mlx5_sync_reset_abort_event);