#ifndef __ARCH_S390_ATOMIC__
#define __ARCH_S390_ATOMIC__

#include <stdint.h>

typedef uint32_t atomic_t;

#define __ATOMIC_OP(op_name, op_type, op_string)                                              \
	static inline op_type op_name(op_type val, op_type *ptr)                              \
	{                                                                                     \
		op_type old, new;                                                             \
                                                                                              \
		asm volatile("0:	lr	%[new],%[old]\n" op_string "	%[new],%[val]\n" \
			     "	cs	%[old],%[new],%[ptr]\n"                                     \
			     "	jl	0b"                                                         \
			     : [old] "=d"(old), [new] "=&d"(new), [ptr] "+Q"(*ptr)            \
			     : [val] "d"(val), "0"(*ptr)                                      \
			     : "cc", "memory");                                               \
		return old;                                                                   \
	}

#define __ATOMIC_OPS(op_name, op_type, op_string) \
	__ATOMIC_OP(op_name, op_type, op_string)  \
	__ATOMIC_OP(op_name##_barrier, op_type, op_string)

__ATOMIC_OPS(__atomic_add, uint32_t, "ar")

#undef __ATOMIC_OPS
#undef __ATOMIC_OP

static inline int atomic_get(const atomic_t *v)
{
	int c;

	asm volatile("	l	%0,%1\n" : "=d"(c) : "Q"(*v));
	return c;
}

static inline void atomic_set(atomic_t *v, int i)
{
	asm volatile("	st	%1,%0\n" : "=Q"(*v) : "d"(i));
}

static inline int atomic_add_return(int i, atomic_t *v)
{
	return __atomic_add_barrier(i, v) + i;
}

static inline void atomic_add(int i, atomic_t *v)
{
	__atomic_add(i, v);
}

#define atomic_sub(_i, _v) atomic_add(-(int)(_i), _v)

static inline int atomic_inc(atomic_t *v)
{
	return atomic_add_return(1, v) - 1;
}

#define atomic_dec(_v) atomic_sub(1, _v)

#endif /* __ARCH_S390_ATOMIC__  */
