return nq_work;
 }
 
+static bool pdsc_adminq_inc_if_up(struct pdsc *pdsc)
+{
+       if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER) ||
+           pdsc->state & BIT_ULL(PDSC_S_FW_DEAD))
+               return false;
+
+       return refcount_inc_not_zero(&pdsc->adminq_refcnt);
+}
+
 void pdsc_process_adminq(struct pdsc_qcq *qcq)
 {
        union pds_core_adminq_comp *comp;
        int aq_work = 0;
        int credits;
 
-       /* Don't process AdminQ when shutting down */
-       if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) {
-               dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n",
+       /* Don't process AdminQ when it's not up */
+       if (!pdsc_adminq_inc_if_up(pdsc)) {
+               dev_err(pdsc->dev, "%s: called while adminq is unavailable\n",
                        __func__);
                return;
        }
                pds_core_intr_credits(&pdsc->intr_ctrl[qcq->intx],
                                      credits,
                                      PDS_CORE_INTR_CRED_REARM);
+       refcount_dec(&pdsc->adminq_refcnt);
 }
 
 void pdsc_work_thread(struct work_struct *work)
        struct pdsc *pdsc = data;
        struct pdsc_qcq *qcq;
 
-       /* Don't process AdminQ when shutting down */
-       if (pdsc->state & BIT_ULL(PDSC_S_STOPPING_DRIVER)) {
-               dev_err(pdsc->dev, "%s: called while PDSC_S_STOPPING_DRIVER\n",
+       /* Don't process AdminQ when it's not up */
+       if (!pdsc_adminq_inc_if_up(pdsc)) {
+               dev_err(pdsc->dev, "%s: called while adminq is unavailable\n",
                        __func__);
                return IRQ_HANDLED;
        }
        qcq = &pdsc->adminqcq;
        queue_work(pdsc->wq, &qcq->work);
        pds_core_intr_mask(&pdsc->intr_ctrl[qcq->intx], PDS_CORE_INTR_MASK_CLEAR);
+       refcount_dec(&pdsc->adminq_refcnt);
 
        return IRQ_HANDLED;
 }
        int err = 0;
        int index;
 
+       if (!pdsc_adminq_inc_if_up(pdsc)) {
+               dev_dbg(pdsc->dev, "%s: preventing adminq cmd %u\n",
+                       __func__, cmd->opcode);
+               return -ENXIO;
+       }
+
        wc.qcq = &pdsc->adminqcq;
        index = __pdsc_adminq_post(pdsc, &pdsc->adminqcq, cmd, comp, &wc);
        if (index < 0) {
                        queue_work(pdsc->wq, &pdsc->health_work);
        }
 
+       refcount_dec(&pdsc->adminq_refcnt);
+
        return err;
 }
 EXPORT_SYMBOL_GPL(pdsc_adminq_post);
 
                pdsc_debugfs_add_viftype(pdsc);
        }
 
+       refcount_set(&pdsc->adminq_refcnt, 1);
        clear_bit(PDSC_S_FW_DEAD, &pdsc->state);
        return 0;
 
                                           PDS_CORE_INTR_MASK_SET);
 }
 
+static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc)
+{
+       /* The driver initializes the adminq_refcnt to 1 when the adminq is
+        * allocated and ready for use. Other users/requesters will increment
+        * the refcnt while in use. If the refcnt is down to 1 then the adminq
+        * is not in use and the refcnt can be cleared and adminq freed. Before
+        * calling this function the driver will set PDSC_S_FW_DEAD, which
+        * prevent subsequent attempts to use the adminq and increment the
+        * refcnt to fail. This guarantees that this function will eventually
+        * exit.
+        */
+       while (!refcount_dec_if_one(&pdsc->adminq_refcnt)) {
+               dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n",
+                                   __func__);
+               cpu_relax();
+       }
+}
+
 void pdsc_fw_down(struct pdsc *pdsc)
 {
        union pds_core_notifyq_comp reset_event = {
        if (pdsc->pdev->is_virtfn)
                return;
 
+       pdsc_adminq_wait_and_dec_once_unused(pdsc);
+
        /* Notify clients of fw_down */
        if (pdsc->fw_reporter)
                devlink_health_report(pdsc->fw_reporter, "FW down reported", pdsc);