/*
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
 *
 * Authors:
 *   Anup Patel <anup.patel@wdc.com>
 */

#ifndef __SBI_HARTMASK_H__
#define __SBI_HARTMASK_H__

#include <sbi/sbi_bitmap.h>

/**
 * Maximum number of bits in a hartmask
 *
 * The hartmask is indexed using physical HART id so this define
 * also represents the maximum number of HART ids generic OpenSBI
 * can handle.
 */
#define SBI_HARTMASK_MAX_BITS		128

/** Representation of hartmask */
struct sbi_hartmask {
	DECLARE_BITMAP(bits, SBI_HARTMASK_MAX_BITS);
};

/** Initialize hartmask to zero */
#define SBI_HARTMASK_INIT(__m)		\
	bitmap_zero(((__m)->bits), SBI_HARTMASK_MAX_BITS)

/** Initialize hartmask to zero except a particular HART id */
#define SBI_HARTMASK_INIT_EXCEPT(__m, __h)	\
	bitmap_zero_except(((__m)->bits), (__h), SBI_HARTMASK_MAX_BITS)

/**
 * Get underlying bitmap of hartmask
 * @param m the hartmask pointer
 */
#define sbi_hartmask_bits(__m)		((__m)->bits)

/**
 * Set a HART in hartmask
 * @param h HART id to set
 * @param m the hartmask pointer
 */
static inline void sbi_hartmask_set_hart(u32 h, struct sbi_hartmask *m)
{
	if (h < SBI_HARTMASK_MAX_BITS)
		__set_bit(h, m->bits);
}

/**
 * Clear a HART in hartmask
 * @param h HART id to clear
 * @param m the hartmask pointer
 */
static inline void sbi_hartmask_clear_hart(u32 h, struct sbi_hartmask *m)
{
	if (h < SBI_HARTMASK_MAX_BITS)
		__clear_bit(h, m->bits);
}

/**
 * Test a HART in hartmask
 * @param h HART id to test
 * @param m the hartmask pointer
 */
static inline int sbi_hartmask_test_hart(u32 h, const struct sbi_hartmask *m)
{
	if (h < SBI_HARTMASK_MAX_BITS)
		return __test_bit(h, m->bits);
	return 0;
}

/**
 * Set all HARTs in a hartmask
 * @param dstp the hartmask pointer
 */
static inline void sbi_hartmask_set_all(struct sbi_hartmask *dstp)
{
	bitmap_fill(sbi_hartmask_bits(dstp), SBI_HARTMASK_MAX_BITS);
}

/**
 * Clear all HARTs in a hartmask
 * @param dstp the hartmask pointer
 */
static inline void sbi_hartmask_clear_all(struct sbi_hartmask *dstp)
{
	bitmap_zero(sbi_hartmask_bits(dstp), SBI_HARTMASK_MAX_BITS);
}

/**
 * *dstp = *src1p & *src2p
 * @param dstp the hartmask result
 * @param src1p the first input
 * @param src2p the second input
 */
static inline void sbi_hartmask_and(struct sbi_hartmask *dstp,
				    const struct sbi_hartmask *src1p,
				    const struct sbi_hartmask *src2p)
{
	bitmap_and(sbi_hartmask_bits(dstp), sbi_hartmask_bits(src1p),
		   sbi_hartmask_bits(src2p), SBI_HARTMASK_MAX_BITS);
}

/**
 * *dstp = *src1p | *src2p
 * @param dstp the hartmask result
 * @param src1p the first input
 * @param src2p the second input
 */
static inline void sbi_hartmask_or(struct sbi_hartmask *dstp,
				   const struct sbi_hartmask *src1p,
				   const struct sbi_hartmask *src2p)
{
	bitmap_or(sbi_hartmask_bits(dstp), sbi_hartmask_bits(src1p),
		  sbi_hartmask_bits(src2p), SBI_HARTMASK_MAX_BITS);
}

/**
 * *dstp = *src1p ^ *src2p
 * @param dstp the hartmask result
 * @param src1p the first input
 * @param src2p the second input
 */
static inline void sbi_hartmask_xor(struct sbi_hartmask *dstp,
				    const struct sbi_hartmask *src1p,
				    const struct sbi_hartmask *src2p)
{
	bitmap_xor(sbi_hartmask_bits(dstp), sbi_hartmask_bits(src1p),
		   sbi_hartmask_bits(src2p), SBI_HARTMASK_MAX_BITS);
}

/** Iterate over each HART in hartmask */
#define sbi_hartmask_for_each_hart(__h, __m)	\
	for_each_set_bit(__h, (__m)->bits, SBI_HARTMASK_MAX_BITS)

#endif