return -EFAULT;
        }
 
+       idev->last_fw_status = 0xff;
        timer_setup(&ionic->watchdog_timer, ionic_watchdog_cb, 0);
        ionic->watchdog_period = IONIC_WATCHDOG_SECS * HZ;
        mod_timer(&ionic->watchdog_timer,
         * fw_status != 0xff (bad PCI read)
         */
        fw_status = ioread8(&idev->dev_info_regs->fw_status);
-       if (fw_status == 0xff ||
-           !(fw_status & IONIC_FW_STS_F_RUNNING))
+       if (fw_status != 0xff)
+               fw_status &= IONIC_FW_STS_F_RUNNING;  /* use only the run bit */
+
+       /* is this a transition? */
+       if (fw_status != idev->last_fw_status &&
+           idev->last_fw_status != 0xff) {
+               struct ionic_lif *lif = ionic->master_lif;
+               bool trigger = false;
+
+               if (!fw_status || fw_status == 0xff) {
+                       dev_info(ionic->dev, "FW stopped %u\n", fw_status);
+                       if (lif && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
+                               trigger = true;
+               } else {
+                       dev_info(ionic->dev, "FW running %u\n", fw_status);
+                       if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state))
+                               trigger = true;
+               }
+
+               if (trigger) {
+                       struct ionic_deferred_work *work;
+
+                       work = kzalloc(sizeof(*work), GFP_ATOMIC);
+                       if (!work) {
+                               dev_err(ionic->dev, "%s OOM\n", __func__);
+                       } else {
+                               work->type = IONIC_DW_TYPE_LIF_RESET;
+                               if (fw_status & IONIC_FW_STS_F_RUNNING &&
+                                   fw_status != 0xff)
+                                       work->fw_status = 1;
+                               ionic_lif_deferred_enqueue(&lif->deferred, work);
+                       }
+               }
+       }
+       idev->last_fw_status = fw_status;
+
+       if (!fw_status || fw_status == 0xff)
                return -ENXIO;
 
        /* early FW has no heartbeat, else FW will return non-zero */
 
 static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr);
 static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr);
 static void ionic_link_status_check(struct ionic_lif *lif);
+static void ionic_lif_handle_fw_down(struct ionic_lif *lif);
+static void ionic_lif_handle_fw_up(struct ionic_lif *lif);
+static void ionic_lif_set_netdev_info(struct ionic_lif *lif);
 
 static int ionic_start_queues(struct ionic_lif *lif);
 static void ionic_stop_queues(struct ionic_lif *lif);
                case IONIC_DW_TYPE_LINK_STATUS:
                        ionic_link_status_check(lif);
                        break;
+               case IONIC_DW_TYPE_LIF_RESET:
+                       if (w->fw_status)
+                               ionic_lif_handle_fw_up(lif);
+                       else
+                               ionic_lif_handle_fw_down(lif);
+                       break;
                default:
                        break;
                }
        }
 }
 
-static void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
-                                      struct ionic_deferred_work *work)
+void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
+                               struct ionic_deferred_work *work)
 {
        spin_lock_bh(&def->lock);
        list_add_tail(&work->list, &def->list);
                                  struct ionic_cq_info *cq_info)
 {
        union ionic_notifyq_comp *comp = cq_info->cq_desc;
+       struct ionic_deferred_work *work;
        struct net_device *netdev;
        struct ionic_queue *q;
        struct ionic_lif *lif;
                ionic_link_status_check_request(lif);
                break;
        case IONIC_EVENT_RESET:
-               netdev_info(netdev, "Notifyq IONIC_EVENT_RESET eid=%lld\n",
-                           eid);
-               netdev_info(netdev, "  reset_code=%d state=%d\n",
-                           comp->reset.reset_code,
-                           comp->reset.state);
+               work = kzalloc(sizeof(*work), GFP_ATOMIC);
+               if (!work) {
+                       netdev_err(lif->netdev, "%s OOM\n", __func__);
+               } else {
+                       work->type = IONIC_DW_TYPE_LIF_RESET;
+                       ionic_lif_deferred_enqueue(&lif->deferred, work);
+               }
                break;
        default:
                netdev_warn(netdev, "Notifyq unknown event ecode=%d eid=%lld\n",
        netdev->hw_features |= netdev->hw_enc_features;
        netdev->features |= netdev->hw_features;
 
-       netdev->priv_flags |= IFF_UNICAST_FLT;
+       netdev->priv_flags |= IFF_UNICAST_FLT |
+                             IFF_LIVE_ADDR_CHANGE;
 
        return 0;
 }
 {
        struct ionic_lif *lif = netdev_priv(netdev);
 
+       if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))
+               return 0;
+
        ionic_stop_queues(lif);
        ionic_txrx_deinit(lif);
        ionic_txrx_free(lif);
        mutex_unlock(&lif->ionic->dev_cmd_lock);
 }
 
+static void ionic_lif_handle_fw_down(struct ionic_lif *lif)
+{
+       struct ionic *ionic = lif->ionic;
+
+       if (test_and_set_bit(IONIC_LIF_F_FW_RESET, lif->state))
+               return;
+
+       dev_info(ionic->dev, "FW Down: Stopping LIFs\n");
+
+       netif_device_detach(lif->netdev);
+
+       if (test_bit(IONIC_LIF_F_UP, lif->state)) {
+               dev_info(ionic->dev, "Surprise FW stop, stopping queues\n");
+               ionic_stop_queues(lif);
+       }
+
+       if (netif_running(lif->netdev)) {
+               ionic_txrx_deinit(lif);
+               ionic_txrx_free(lif);
+       }
+       ionic_lifs_deinit(ionic);
+       ionic_qcqs_free(lif);
+
+       dev_info(ionic->dev, "FW Down: LIFs stopped\n");
+}
+
+static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
+{
+       struct ionic *ionic = lif->ionic;
+       int err;
+
+       if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
+               return;
+
+       dev_info(ionic->dev, "FW Up: restarting LIFs\n");
+
+       err = ionic_qcqs_alloc(lif);
+       if (err)
+               goto err_out;
+
+       err = ionic_lifs_init(ionic);
+       if (err)
+               goto err_qcqs_free;
+
+       if (lif->registered)
+               ionic_lif_set_netdev_info(lif);
+
+       if (netif_running(lif->netdev)) {
+               err = ionic_txrx_alloc(lif);
+               if (err)
+                       goto err_lifs_deinit;
+
+               err = ionic_txrx_init(lif);
+               if (err)
+                       goto err_txrx_free;
+       }
+
+       clear_bit(IONIC_LIF_F_FW_RESET, lif->state);
+       ionic_link_status_check_request(lif);
+       netif_device_attach(lif->netdev);
+       dev_info(ionic->dev, "FW Up: LIFs restarted\n");
+
+       return;
+
+err_txrx_free:
+       ionic_txrx_free(lif);
+err_lifs_deinit:
+       ionic_lifs_deinit(ionic);
+err_qcqs_free:
+       ionic_qcqs_free(lif);
+err_out:
+       dev_err(ionic->dev, "FW Up: LIFs restart failed - err %d\n", err);
+}
+
 static void ionic_lif_free(struct ionic_lif *lif)
 {
        struct device *dev = lif->ionic->dev;
 
        /* free queues */
        ionic_qcqs_free(lif);
-       ionic_lif_reset(lif);
+       if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
+               ionic_lif_reset(lif);
 
        /* free lif info */
        dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
 
 static void ionic_lif_deinit(struct ionic_lif *lif)
 {
-       if (!test_bit(IONIC_LIF_F_INITED, lif->state))
+       if (!test_and_clear_bit(IONIC_LIF_F_INITED, lif->state))
                return;
 
-       clear_bit(IONIC_LIF_F_INITED, lif->state);
+       if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
+               cancel_work_sync(&lif->deferred.work);
+               cancel_work_sync(&lif->tx_timeout_work);
+       }
 
        ionic_rx_filters_deinit(lif);
        if (lif->netdev->features & NETIF_F_RXHASH)
                ionic_lif_rss_deinit(lif);
 
        napi_disable(&lif->adminqcq->napi);
-       netif_napi_del(&lif->adminqcq->napi);
        ionic_lif_qcq_deinit(lif, lif->notifyqcq);
        ionic_lif_qcq_deinit(lif, lif->adminqcq);
 
        if (err)
                return err;
 
+       lif->last_eid = 0;
        q->hw_type = ctx.comp.q_init.hw_type;
        q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
        q->dbval = IONIC_DBELL_QID(q->hw_index);
        addr.sa_family = AF_INET;
        err = eth_prepare_mac_addr_change(netdev, &addr);
        if (err) {
-               netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM\n",
-                           addr.sa_data);
+               netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM - err %d\n",
+                           addr.sa_data, err);
                return 0;
        }
 
         * current model, so don't bother searching the
         * ionic->lif for candidates to unregister
         */
-       if (!ionic->master_lif)
-               return;
-
-       cancel_work_sync(&ionic->master_lif->deferred.work);
-       cancel_work_sync(&ionic->master_lif->tx_timeout_work);
-       if (ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
+       if (ionic->master_lif &&
+           ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
                unregister_netdev(ionic->master_lif->netdev);
 }