/*
 * Copyright (c) 2020 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
 * OS-Lab-2020 (i.e., ChCore) is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 *   http://license.coscl.org.cn/MulanPSL
 *   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 *   PURPOSE.
 *   See the Mulan PSL v1 for more details.
 */

#pragma once

#include <common/types.h>

#define COMPILER_BARRIER() asm volatile("":::"memory")

#define sev()		asm volatile("sev" : : : "memory")
#define wfe()		asm volatile("wfe" : : : "memory")
#define wfi()		asm volatile("wfi" : : : "memory")

#define isb()		asm volatile("isb" : : : "memory")
#define dmb(opt)	asm volatile("dmb " #opt : : : "memory")
#define dsb(opt)	asm volatile("dsb " #opt : : : "memory")

#define mb()		dsb(sy)
#define rmb()		dsb(ld)
#define wmb()		dsb(st)

#define smp_mb()	dmb(ish)
#define smp_rmb()	smp_mb()
#define smp_wmb()	dmb(ishst)

#define dma_rmb()	dmb(oshld)
#define dma_wmb()	dmb(oshst)

#define ldar_32(ptr, value) asm volatile("ldar %w0, [%1]" : "=r" (value) : "r" (ptr))
#define stlr_32(ptr, value) asm volatile("stlr %w0, [%1]" : : "rZ" (value) , "r" (ptr))

#define ldar_64(ptr, value) asm volatile("ldar %x0, [%1]" : "=r" (value) : "r" (ptr))
#define stlr_64(ptr, value) asm volatile("stlr %x0, [%1]" : : "rZ" (value) , "r" (ptr))

#define __atomic_compare_exchange(ptr, compare, exchange, len, width)	\
({									\
	u##len oldval;							\
	u32 ret;							\
	asm volatile ("1: ldaxr   %"#width"0, %2\n"			\
		      "   cmp     %"#width"0, %"#width"3\n"		\
		      "   b.ne    2f\n"					\
		      "   stlxr   %w1, %"#width"4, %2\n"		\
		      "   cbnz    %w1, 1b\n"				\
		      "2:":"=&r" (oldval), "=&r"(ret), "+Q"(*ptr)	\
		      :"r"(compare), "r"(exchange)			\
	    );								\
	oldval;								\
})

#define atomic_compare_exchange_64(ptr, compare, exchange) \
	__atomic_compare_exchange(ptr, compare, exchange, 64, x)
#define atomic_compare_exchange_32(ptr, compare, exchange) \
	__atomic_compare_exchange(ptr, compare, exchange, 32, w)

static inline s64 atomic_exchange_64(s64 * ptr, s64 exchange)
{
	s64 oldval;
	s32 ret;
	asm volatile ("1: ldaxr   %x0, %2\n"
		      "   stlxr   %w1, %x3, %2\n"
		      "   cbnz    %w1, 1b\n"
		      "2:":"=&r" (oldval), "=&r"(ret), "+Q"(*ptr)
		      :"r"(exchange)
	    );
	return oldval;
}

#define __atomic_fetch_op(ptr, val, len, width, op)			\
({									\
	u##len oldval, newval;						\
	u32 ret;							\
	asm volatile ("1: ldaxr   %"#width"0, %3\n"			\
		      "   "#op"   %"#width"1, %"#width"0, %"#width"4\n"	\
		      "   stlxr   %w2, %"#width"1, %3\n"		\
		      "   cbnz    %w2, 1b\n"				\
		      "2:":"=&r" (oldval), "=&r"(newval),		\
		      "=&r"(ret), "+Q"(*ptr)				\
		      :"r"(val)						\
	    );								\
	oldval;								\
 })

#define atomic_fetch_sub_32(ptr, val) __atomic_fetch_op(ptr, val, 32, w, sub)
#define atomic_fetch_sub_64(ptr, val) __atomic_fetch_op(ptr, val, 64, x, sub)
#define atomic_fetch_add_32(ptr, val) __atomic_fetch_op(ptr, val, 32, w, add)
#define atomic_fetch_add_64(ptr, val) __atomic_fetch_op(ptr, val, 64, x, add)
#define atomic_set_bit_32(ptr, val)   __atomic_fetch_op(ptr, 1<<(val), 32, w, or)