static void ionic_qcqs_free(struct ionic_lif *lif)
 {
+       if (lif->notifyqcq) {
+               ionic_qcq_free(lif, lif->notifyqcq);
+               lif->notifyqcq = NULL;
+       }
+
        if (lif->adminqcq) {
                ionic_qcq_free(lif, lif->adminqcq);
                lif->adminqcq = NULL;
        }
 }
 
+static void ionic_link_qcq_interrupts(struct ionic_qcq *src_qcq,
+                                     struct ionic_qcq *n_qcq)
+{
+       if (WARN_ON(n_qcq->flags & IONIC_QCQ_F_INTR)) {
+               ionic_intr_free(n_qcq->cq.lif, n_qcq->intr.index);
+               n_qcq->flags &= ~IONIC_QCQ_F_INTR;
+       }
+
+       n_qcq->intr.vector = src_qcq->intr.vector;
+       n_qcq->intr.index = src_qcq->intr.index;
+}
+
 static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
                           unsigned int index,
                           const char *name, unsigned int flags,
        if (err)
                return err;
 
+       if (lif->ionic->nnqs_per_lif) {
+               flags = IONIC_QCQ_F_NOTIFYQ;
+               err = ionic_qcq_alloc(lif, IONIC_QTYPE_NOTIFYQ, 0, "notifyq",
+                                     flags, IONIC_NOTIFYQ_LENGTH,
+                                     sizeof(struct ionic_notifyq_cmd),
+                                     sizeof(union ionic_notifyq_comp),
+                                     0, lif->kern_pid, &lif->notifyqcq);
+               if (err)
+                       goto err_out_free_adminqcq;
+
+               /* Let the notifyq ride on the adminq interrupt */
+               ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq);
+       }
+
        return 0;
+
+err_out_free_adminqcq:
+       ionic_qcq_free(lif, lif->adminqcq);
+       lif->adminqcq = NULL;
+
+       return err;
+}
+
+static bool ionic_notifyq_service(struct ionic_cq *cq,
+                                 struct ionic_cq_info *cq_info)
+{
+       union ionic_notifyq_comp *comp = cq_info->cq_desc;
+       struct net_device *netdev;
+       struct ionic_queue *q;
+       struct ionic_lif *lif;
+       u64 eid;
+
+       q = cq->bound_q;
+       lif = q->info[0].cb_arg;
+       netdev = lif->netdev;
+       eid = le64_to_cpu(comp->event.eid);
+
+       /* Have we run out of new completions to process? */
+       if (eid <= lif->last_eid)
+               return false;
+
+       lif->last_eid = eid;
+
+       dev_dbg(lif->ionic->dev, "notifyq event:\n");
+       dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1,
+                        comp, sizeof(*comp), true);
+
+       switch (le16_to_cpu(comp->event.ecode)) {
+       case IONIC_EVENT_LINK_CHANGE:
+               netdev_info(netdev, "Notifyq IONIC_EVENT_LINK_CHANGE eid=%lld\n",
+                           eid);
+               netdev_info(netdev,
+                           "  link_status=%d link_speed=%d\n",
+                           le16_to_cpu(comp->link_change.link_status),
+                           le32_to_cpu(comp->link_change.link_speed));
+               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);
+               break;
+       default:
+               netdev_warn(netdev, "Notifyq unknown event ecode=%d eid=%lld\n",
+                           comp->event.ecode, eid);
+               break;
+       }
+
+       return true;
+}
+
+static int ionic_notifyq_clean(struct ionic_lif *lif, int budget)
+{
+       struct ionic_dev *idev = &lif->ionic->idev;
+       struct ionic_cq *cq = &lif->notifyqcq->cq;
+       u32 work_done;
+
+       work_done = ionic_cq_service(cq, budget, ionic_notifyq_service,
+                                    NULL, NULL);
+       if (work_done)
+               ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index,
+                                  work_done, IONIC_INTR_CRED_RESET_COALESCE);
+
+       return work_done;
 }
 
 static bool ionic_adminq_service(struct ionic_cq *cq,
 
 static int ionic_adminq_napi(struct napi_struct *napi, int budget)
 {
-       return ionic_napi(napi, budget, ionic_adminq_service, NULL, NULL);
+       struct ionic_lif *lif = napi_to_cq(napi)->lif;
+       int n_work = 0;
+       int a_work = 0;
+
+       if (likely(lif->notifyqcq && lif->notifyqcq->flags & IONIC_QCQ_F_INITED))
+               n_work = ionic_notifyq_clean(lif, budget);
+       a_work = ionic_napi(napi, budget, ionic_adminq_service, NULL, NULL);
+
+       return max(n_work, a_work);
 }
 
 static struct ionic_lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
        clear_bit(IONIC_LIF_INITED, lif->state);
 
        napi_disable(&lif->adminqcq->napi);
+       ionic_lif_qcq_deinit(lif, lif->notifyqcq);
        ionic_lif_qcq_deinit(lif, lif->adminqcq);
 
        ionic_lif_reset(lif);
        return 0;
 }
 
+static int ionic_lif_notifyq_init(struct ionic_lif *lif)
+{
+       struct ionic_qcq *qcq = lif->notifyqcq;
+       struct device *dev = lif->ionic->dev;
+       struct ionic_queue *q = &qcq->q;
+       int err;
+
+       struct ionic_admin_ctx ctx = {
+               .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+               .cmd.q_init = {
+                       .opcode = IONIC_CMD_Q_INIT,
+                       .lif_index = cpu_to_le16(lif->index),
+                       .type = q->type,
+                       .index = cpu_to_le32(q->index),
+                       .flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
+                                            IONIC_QINIT_F_ENA),
+                       .intr_index = cpu_to_le16(lif->adminqcq->intr.index),
+                       .pid = cpu_to_le16(q->pid),
+                       .ring_size = ilog2(q->num_descs),
+                       .ring_base = cpu_to_le64(q->base_pa),
+               }
+       };
+
+       dev_dbg(dev, "notifyq_init.pid %d\n", ctx.cmd.q_init.pid);
+       dev_dbg(dev, "notifyq_init.index %d\n", ctx.cmd.q_init.index);
+       dev_dbg(dev, "notifyq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
+       dev_dbg(dev, "notifyq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
+
+       err = ionic_adminq_post_wait(lif, &ctx);
+       if (err)
+               return err;
+
+       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);
+
+       dev_dbg(dev, "notifyq->hw_type %d\n", q->hw_type);
+       dev_dbg(dev, "notifyq->hw_index %d\n", q->hw_index);
+
+       /* preset the callback info */
+       q->info[0].cb_arg = lif;
+
+       qcq->flags |= IONIC_QCQ_F_INITED;
+
+       ionic_debugfs_add_qcq(lif, qcq);
+
+       return 0;
+}
+
 static int ionic_lif_init(struct ionic_lif *lif)
 {
        struct ionic_dev *idev = &lif->ionic->idev;
        if (err)
                goto err_out_adminq_deinit;
 
+       if (lif->ionic->nnqs_per_lif) {
+               err = ionic_lif_notifyq_init(lif);
+               if (err)
+                       goto err_out_notifyq_deinit;
+       }
+
        set_bit(IONIC_LIF_INITED, lif->state);
 
        return 0;
 
+err_out_notifyq_deinit:
+       ionic_lif_qcq_deinit(lif, lif->notifyqcq);
 err_out_adminq_deinit:
        ionic_lif_qcq_deinit(lif, lif->adminqcq);
        ionic_lif_reset(lif);