// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/printk.h>
#include "ionic_fw.h"
#include "ionic_ibdev.h"
#define IONIC_EQ_COUNT_MIN 4
#define IONIC_AQ_COUNT_MIN 1
/* not a valid queue position or negative error status */
#define IONIC_ADMIN_POSTED 0x10000
/* cpu can be held with irq disabled for COUNT * MS (for create/destroy_ah) */
#define IONIC_ADMIN_BUSY_RETRY_COUNT 2000
#define IONIC_ADMIN_BUSY_RETRY_MS 1
/* admin queue will be considered failed if a command takes longer */
#define IONIC_ADMIN_TIMEOUT (HZ * 2)
#define IONIC_ADMIN_WARN (HZ / 8)
/* will poll for admin cq to tolerate and report from missed event */
#define IONIC_ADMIN_DELAY (HZ / 8)
/* work queue for polling the event queue and admin cq */
struct workqueue_struct *ionic_evt_workq;
static void ionic_admin_timedout(struct ionic_aq *aq)
{
struct ionic_ibdev *dev = aq->dev;
unsigned long irqflags;
u16 pos;
spin_lock_irqsave(&aq->lock, irqflags);
if (ionic_queue_empty(&aq->q))
goto out;
/* Reset ALL adminq if any one times out */
if (atomic_read(&aq->admin_state) < IONIC_ADMIN_KILLED)
queue_work(ionic_evt_workq, &dev->reset_work);
ibdev_err(&dev->ibdev, "admin command timed out, aq %d after: %ums\n",
aq->aqid, (u32)jiffies_to_msecs(jiffies - aq->stamp));
pos = (aq->q.prod - 1) & aq->q.mask;
if (pos == aq->q.cons)
goto out;
ibdev_warn(&dev->ibdev, "admin pos %u (last posted)\n", pos);
print_hex_dump(KERN_WARNING, "cmd ", DUMP_PREFIX_OFFSET, 16, 1,
ionic_queue_at(&aq->q, pos),
BIT(aq->q.stride_log2), true);
out:
spin_unlock_irqrestore(&aq->lock, irqflags);
}
static void ionic_admin_reset_dwork(struct ionic_ibdev *dev)
{
if (atomic_read(&dev->admin_state) == IONIC_ADMIN_KILLED)
return;
queue_delayed_work(ionic_evt_workq, &dev->admin_dwork,
IONIC_ADMIN_DELAY);
}
static void ionic_admin_reset_wdog(struct ionic_aq *aq)
{
if (atomic_read(&aq->admin_state) == IONIC_ADMIN_KILLED)
return;
aq->stamp = jiffies;
ionic_admin_reset_dwork(aq->dev);
}
static bool ionic_admin_next_cqe(struct ionic_ibdev *dev, struct ionic_cq *cq,
struct ionic_v1_cqe **cqe)
{
struct ionic_v1_cqe *qcqe = ionic_queue_at_prod(&cq->q);
if (unlikely(cq->color != ionic_v1_cqe_color(qcqe)))
return false;
/* Prevent out-of-order reads of the CQE */
dma_rmb();
*cqe = qcqe;
return true;
}
static void ionic_admin_poll_locked(struct ionic_aq *aq)