root/drivers/net/ipa/reg.h
/* SPDX-License-Identifier: GPL-2.0 */

/* Copyright (C) 2022-2024 Linaro Ltd. */

#ifndef _REG_H_
#define _REG_H_

#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/bug.h>
#include <linux/log2.h>
#include <linux/types.h>

/**
 * struct reg - A register descriptor
 * @offset:     Register offset relative to base of register memory
 * @stride:     Distance between two instances, if parameterized
 * @fcount:     Number of entries in the @fmask array
 * @fmask:      Array of mask values defining position and width of fields
 * @name:       Upper-case name of the register
 */
struct reg {
        u32 offset;
        u32 stride;
        u32 fcount;
        const u32 *fmask;                       /* BIT(nr) or GENMASK(h, l) */
        const char *name;
};

/* Helper macro for defining "simple" (non-parameterized) registers */
#define REG(__NAME, __reg_id, __offset)                                 \
        REG_STRIDE(__NAME, __reg_id, __offset, 0)

/* Helper macro for defining parameterized registers, specifying stride */
#define REG_STRIDE(__NAME, __reg_id, __offset, __stride)                \
        static const struct reg reg_ ## __reg_id = {                    \
                .name   = #__NAME,                                      \
                .offset = __offset,                                     \
                .stride = __stride,                                     \
        }

#define REG_FIELDS(__NAME, __name, __offset)                            \
        REG_STRIDE_FIELDS(__NAME, __name, __offset, 0)

#define REG_STRIDE_FIELDS(__NAME, __name, __offset, __stride)           \
        static const struct reg reg_ ## __name = {                      \
                .name   = #__NAME,                                      \
                .offset = __offset,                                     \
                .stride = __stride,                                     \
                .fcount = ARRAY_SIZE(reg_ ## __name ## _fmask),         \
                .fmask  = reg_ ## __name ## _fmask,                     \
        }

/**
 * struct regs - Description of registers supported by hardware
 * @reg_count:  Number of registers in the @reg[] array
 * @reg:        Array of register descriptors
 */
struct regs {
        u32 reg_count;
        const struct reg **reg;
};

static inline const struct reg *reg(const struct regs *regs, u32 reg_id)
{
        if (WARN(reg_id >= regs->reg_count,
                 "reg out of range (%u > %u)\n", reg_id, regs->reg_count - 1))
                return NULL;

        return regs->reg[reg_id];
}

/* Return the field mask for a field in a register, or 0 on error */
static inline u32 reg_fmask(const struct reg *reg, u32 field_id)
{
        if (!reg || WARN_ON(field_id >= reg->fcount))
                return 0;

        return reg->fmask[field_id];
}

/* Return the mask for a single-bit field in a register, or 0 on error  */
static inline u32 reg_bit(const struct reg *reg, u32 field_id)
{
        u32 fmask = reg_fmask(reg, field_id);

        if (WARN_ON(!is_power_of_2(fmask)))
                return 0;

        return fmask;
}

/* Return the maximum value representable by the given field; always 2^n - 1 */
static inline u32 reg_field_max(const struct reg *reg, u32 field_id)
{
        u32 fmask = reg_fmask(reg, field_id);

        return fmask ? fmask >> __ffs(fmask) : 0;
}

/* Encode a value into the given field of a register */
static inline u32 reg_encode(const struct reg *reg, u32 field_id, u32 val)
{
        u32 fmask = reg_fmask(reg, field_id);

        if (!fmask)
                return 0;

        val <<= __ffs(fmask);
        if (WARN_ON(val & ~fmask))
                return 0;

        return val;
}

/* Given a register value, decode (extract) the value in the given field */
static inline u32 reg_decode(const struct reg *reg, u32 field_id, u32 val)
{
        u32 fmask = reg_fmask(reg, field_id);

        return fmask ? (val & fmask) >> __ffs(fmask) : 0;
}

/* Returns 0 for NULL reg; warning should have already been issued */
static inline u32 reg_offset(const struct reg *reg)
{
        return reg ? reg->offset : 0;
}

/* Returns 0 for NULL reg; warning should have already been issued */
static inline u32 reg_n_offset(const struct reg *reg, u32 n)
{
        return reg ? reg->offset + n * reg->stride : 0;
}

#endif /* _REG_H_ */