root/drivers/infiniband/hw/ionic/ionic_queue.h
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */

#ifndef _IONIC_QUEUE_H_
#define _IONIC_QUEUE_H_

#include <linux/io.h>
#include <ionic_regs.h>

#define IONIC_MAX_DEPTH         0xffff
#define IONIC_MAX_CQ_DEPTH      0xffff
#define IONIC_CQ_RING_ARM       IONIC_DBELL_RING_1
#define IONIC_CQ_RING_SOL       IONIC_DBELL_RING_2

/**
 * struct ionic_queue - Ring buffer used between device and driver
 * @size:       Size of the buffer, in bytes
 * @dma:        Dma address of the buffer
 * @ptr:        Buffer virtual address
 * @prod:       Driver position in the queue
 * @cons:       Device position in the queue
 * @mask:       Capacity of the queue, subtracting the hole
 *              This value is equal to ((1 << depth_log2) - 1)
 * @depth_log2: Log base two size depth of the queue
 * @stride_log2: Log base two size of an element in the queue
 * @dbell:      Doorbell identifying bits
 */
struct ionic_queue {
        size_t size;
        dma_addr_t dma;
        void *ptr;
        u16 prod;
        u16 cons;
        u16 mask;
        u8 depth_log2;
        u8 stride_log2;
        u64 dbell;
};

/**
 * ionic_queue_init() - Initialize user space queue
 * @q:          Uninitialized queue structure
 * @dma_dev:    DMA device for mapping
 * @depth:      Depth of the queue
 * @stride:     Size of each element of the queue
 *
 * Return: status code
 */
int ionic_queue_init(struct ionic_queue *q, struct device *dma_dev,
                     int depth, size_t stride);

/**
 * ionic_queue_destroy() - Destroy user space queue
 * @q:          Queue structure
 * @dma_dev:    DMA device for mapping
 *
 * Return: status code
 */
void ionic_queue_destroy(struct ionic_queue *q, struct device *dma_dev);

/**
 * ionic_queue_empty() - Test if queue is empty
 * @q:          Queue structure
 *
 * This is only valid for to-device queues.
 *
 * Return: is empty
 */
static inline bool ionic_queue_empty(struct ionic_queue *q)
{
        return q->prod == q->cons;
}

/**
 * ionic_queue_length() - Get the current length of the queue
 * @q:          Queue structure
 *
 * This is only valid for to-device queues.
 *
 * Return: length
 */
static inline u16 ionic_queue_length(struct ionic_queue *q)
{
        return (q->prod - q->cons) & q->mask;
}

/**
 * ionic_queue_length_remaining() - Get the remaining length of the queue
 * @q:          Queue structure
 *
 * This is only valid for to-device queues.
 *
 * Return: length remaining
 */
static inline u16 ionic_queue_length_remaining(struct ionic_queue *q)
{
        return q->mask - ionic_queue_length(q);
}

/**
 * ionic_queue_full() - Test if queue is full
 * @q:          Queue structure
 *
 * This is only valid for to-device queues.
 *
 * Return: is full
 */
static inline bool ionic_queue_full(struct ionic_queue *q)
{
        return q->mask == ionic_queue_length(q);
}

/**
 * ionic_color_wrap() - Flip the color if prod is wrapped
 * @prod:       Queue index just after advancing
 * @color:      Queue color just prior to advancing the index
 *
 * Return: color after advancing the index
 */
static inline bool ionic_color_wrap(u16 prod, bool color)
{
        /* logical xor color with (prod == 0) */
        return color != (prod == 0);
}

/**
 * ionic_queue_at() - Get the element at the given index
 * @q:          Queue structure
 * @idx:        Index in the queue
 *
 * The index must be within the bounds of the queue.  It is not checked here.
 *
 * Return: pointer to element at index
 */
static inline void *ionic_queue_at(struct ionic_queue *q, u16 idx)
{
        return q->ptr + ((unsigned long)idx << q->stride_log2);
}

/**
 * ionic_queue_at_prod() - Get the element at the producer index
 * @q:          Queue structure
 *
 * Return: pointer to element at producer index
 */
static inline void *ionic_queue_at_prod(struct ionic_queue *q)
{
        return ionic_queue_at(q, q->prod);
}

/**
 * ionic_queue_at_cons() - Get the element at the consumer index
 * @q:          Queue structure
 *
 * Return: pointer to element at consumer index
 */
static inline void *ionic_queue_at_cons(struct ionic_queue *q)
{
        return ionic_queue_at(q, q->cons);
}

/**
 * ionic_queue_next() - Compute the next index
 * @q:          Queue structure
 * @idx:        Index
 *
 * Return: next index after idx
 */
static inline u16 ionic_queue_next(struct ionic_queue *q, u16 idx)
{
        return (idx + 1) & q->mask;
}

/**
 * ionic_queue_produce() - Increase the producer index
 * @q:          Queue structure
 *
 * Caller must ensure that the queue is not full.  It is not checked here.
 */
static inline void ionic_queue_produce(struct ionic_queue *q)
{
        q->prod = ionic_queue_next(q, q->prod);
}

/**
 * ionic_queue_consume() - Increase the consumer index
 * @q:          Queue structure
 *
 * Caller must ensure that the queue is not empty.  It is not checked here.
 *
 * This is only valid for to-device queues.
 */
static inline void ionic_queue_consume(struct ionic_queue *q)
{
        q->cons = ionic_queue_next(q, q->cons);
}

/**
 * ionic_queue_consume_entries() - Increase the consumer index by entries
 * @q:                          Queue structure
 * @entries:            Number of entries to increment
 *
 * Caller must ensure that the queue is not empty.  It is not checked here.
 *
 * This is only valid for to-device queues.
 */
static inline void ionic_queue_consume_entries(struct ionic_queue *q,
                                               u16 entries)
{
        q->cons = (q->cons + entries) & q->mask;
}

/**
 * ionic_queue_dbell_init() - Initialize doorbell bits for queue id
 * @q:          Queue structure
 * @qid:        Queue identifying number
 */
static inline void ionic_queue_dbell_init(struct ionic_queue *q, u32 qid)
{
        q->dbell = IONIC_DBELL_QID(qid);
}

/**
 * ionic_queue_dbell_val() - Get current doorbell update value
 * @q:          Queue structure
 *
 * Return: current doorbell update value
 */
static inline u64 ionic_queue_dbell_val(struct ionic_queue *q)
{
        return q->dbell | q->prod;
}

#endif /* _IONIC_QUEUE_H_ */