/*
 * Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*
 * @file	gcc/atomic.h
 * @brief	GCC specific atomic primitives for libmetal.
 */

#ifndef __METAL_GCC_ATOMIC__H__
#define __METAL_GCC_ATOMIC__H__

#ifdef __cplusplus
extern "C" {
#endif

typedef int atomic_flag;
typedef char atomic_char;
typedef unsigned char atomic_uchar;
typedef short atomic_short;
typedef unsigned short atomic_ushort;
typedef int atomic_int;
typedef unsigned int atomic_uint;
typedef atomic_uint atomic_uintptr_t;
typedef long atomic_long;
typedef unsigned long atomic_ulong;
typedef long long atomic_llong;
typedef unsigned long long atomic_ullong;

#define ATOMIC_FLAG_INIT	0
#define ATOMIC_VAR_INIT(VAL)	(VAL)

typedef enum {
	memory_order_relaxed,
	memory_order_consume,
	memory_order_acquire,
	memory_order_release,
	memory_order_acq_rel,
	memory_order_seq_cst,
} memory_order;

#define atomic_flag_test_and_set(FLAG)					\
	__sync_lock_test_and_set((FLAG), 1)
#define atomic_flag_test_and_set_explicit(FLAG, MO)			\
	atomic_flag_test_and_set(FLAG)
#define atomic_flag_clear(FLAG)						\
	__sync_lock_release((FLAG))
#define atomic_flag_clear_explicit(FLAG, MO)				\
	atomic_flag_clear(FLAG)
#define atomic_init(OBJ, VAL)						\
	do { *(OBJ) = (VAL); } while (0)
#define atomic_is_lock_free(OBJ)					\
	(sizeof(*(OBJ)) <= sizeof(long))
#define atomic_store(OBJ, VAL)						\
	do { *(OBJ) = (VAL); __sync_synchronize(); } while (0)
#define atomic_store_explicit(OBJ, VAL, MO)				\
	atomic_store((OBJ), (VAL))
#define atomic_load(OBJ)						\
	({ __sync_synchronize(); *(OBJ); })
#define atomic_load_explicit(OBJ, MO)					\
	atomic_load(OBJ)
#define atomic_exchange(OBJ, DES)					\
	({								\
		__typeof__(OBJ) obj = (OBJ);				\
		__typeof__(*obj) des = (DES);				\
		__typeof__(*obj) expval;					\
		__typeof__(*obj) oldval = atomic_load(obj);			\
		do {							\
			expval = oldval;				\
			oldval = __sync_val_compare_and_swap(		\
				obj, expval, des);			\
		} while (oldval != expval);				\
		oldval;							\
	})
#define atomic_exchange_explicit(OBJ, DES, MO)				\
	atomic_exchange((OBJ), (DES))
#define atomic_compare_exchange_strong(OBJ, EXP, DES)			\
	({								\
		__typeof__(OBJ) obj = (OBJ);				\
		__typeof__(EXP) exp = (EXP);				\
		__typeof__(*obj) expval = *exp;				\
		__typeof__(*obj) oldval = __sync_val_compare_and_swap(	\
			obj, expval, (DES));				\
		*exp = oldval;						\
		oldval == expval;					\
	})
#define atomic_compare_exchange_strong_explicit(OBJ, EXP, DES, MO)	\
	atomic_compare_exchange_strong((OBJ), (EXP), (DES))
#define atomic_compare_exchange_weak(OBJ, EXP, DES)			\
	atomic_compare_exchange_strong((OBJ), (EXP), (DES))
#define atomic_compare_exchange_weak_explicit(OBJ, EXP, DES, MO)	\
	atomic_compare_exchange_weak((OBJ), (EXP), (DES))
#define atomic_fetch_add(OBJ, VAL)					\
	__sync_fetch_and_add((OBJ), (VAL))
#define atomic_fetch_add_explicit(OBJ, VAL, MO)				\
	atomic_fetch_add((OBJ), (VAL))
#define atomic_fetch_sub(OBJ, VAL)					\
	__sync_fetch_and_sub((OBJ), (VAL))
#define atomic_fetch_sub_explicit(OBJ, VAL, MO)				\
	atomic_fetch_sub((OBJ), (VAL))
#define atomic_fetch_or(OBJ, VAL)					\
	__sync_fetch_and_or((OBJ), (VAL))
#define atomic_fetch_or_explicit(OBJ, VAL, MO)				\
	atomic_fetch_or((OBJ), (VAL))
#define atomic_fetch_xor(OBJ, VAL)					\
	__sync_fetch_and_xor((OBJ), (VAL))
#define atomic_fetch_xor_explicit(OBJ, VAL, MO)				\
	atomic_fetch_xor((OBJ), (VAL))
#define atomic_fetch_and(OBJ, VAL)					\
	__sync_fetch_and_and((OBJ), (VAL))
#define atomic_fetch_and_explicit(OBJ, VAL, MO)				\
	atomic_fetch_and((OBJ), (VAL))
#define atomic_thread_fence(MO)						\
	__sync_synchronize()
#define atomic_signal_fence(MO)						\
	__sync_synchronize()

#ifdef __cplusplus
}
#endif

#endif /* __METAL_GCC_ATOMIC__H__ */
