162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#ifndef __ASM_ARC_CMPXCHG_H
762306a36Sopenharmony_ci#define __ASM_ARC_CMPXCHG_H
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/build_bug.h>
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <asm/barrier.h>
1362306a36Sopenharmony_ci#include <asm/smp.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#ifdef CONFIG_ARC_HAS_LLSC
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * if (*ptr == @old)
1962306a36Sopenharmony_ci *      *ptr = @new
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci#define __cmpxchg(ptr, old, new)					\
2262306a36Sopenharmony_ci({									\
2362306a36Sopenharmony_ci	__typeof__(*(ptr)) _prev;					\
2462306a36Sopenharmony_ci									\
2562306a36Sopenharmony_ci	__asm__ __volatile__(						\
2662306a36Sopenharmony_ci	"1:	llock  %0, [%1]	\n"					\
2762306a36Sopenharmony_ci	"	brne   %0, %2, 2f	\n"				\
2862306a36Sopenharmony_ci	"	scond  %3, [%1]	\n"					\
2962306a36Sopenharmony_ci	"	bnz     1b		\n"				\
3062306a36Sopenharmony_ci	"2:				\n"				\
3162306a36Sopenharmony_ci	: "=&r"(_prev)	/* Early clobber prevent reg reuse */		\
3262306a36Sopenharmony_ci	: "r"(ptr),	/* Not "m": llock only supports reg */		\
3362306a36Sopenharmony_ci	  "ir"(old),							\
3462306a36Sopenharmony_ci	  "r"(new)	/* Not "ir": scond can't take LIMM */		\
3562306a36Sopenharmony_ci	: "cc",								\
3662306a36Sopenharmony_ci	  "memory");	/* gcc knows memory is clobbered */		\
3762306a36Sopenharmony_ci									\
3862306a36Sopenharmony_ci	_prev;								\
3962306a36Sopenharmony_ci})
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define arch_cmpxchg_relaxed(ptr, old, new)				\
4262306a36Sopenharmony_ci({									\
4362306a36Sopenharmony_ci	__typeof__(ptr) _p_ = (ptr);					\
4462306a36Sopenharmony_ci	__typeof__(*(ptr)) _o_ = (old);					\
4562306a36Sopenharmony_ci	__typeof__(*(ptr)) _n_ = (new);					\
4662306a36Sopenharmony_ci	__typeof__(*(ptr)) _prev_;					\
4762306a36Sopenharmony_ci									\
4862306a36Sopenharmony_ci	switch(sizeof((_p_))) {						\
4962306a36Sopenharmony_ci	case 4:								\
5062306a36Sopenharmony_ci		_prev_ = __cmpxchg(_p_, _o_, _n_);			\
5162306a36Sopenharmony_ci		break;							\
5262306a36Sopenharmony_ci	default:							\
5362306a36Sopenharmony_ci		BUILD_BUG();						\
5462306a36Sopenharmony_ci	}								\
5562306a36Sopenharmony_ci	_prev_;								\
5662306a36Sopenharmony_ci})
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#else
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define arch_cmpxchg(ptr, old, new)				        \
6162306a36Sopenharmony_ci({									\
6262306a36Sopenharmony_ci	volatile __typeof__(ptr) _p_ = (ptr);				\
6362306a36Sopenharmony_ci	__typeof__(*(ptr)) _o_ = (old);					\
6462306a36Sopenharmony_ci	__typeof__(*(ptr)) _n_ = (new);					\
6562306a36Sopenharmony_ci	__typeof__(*(ptr)) _prev_;					\
6662306a36Sopenharmony_ci	unsigned long __flags;						\
6762306a36Sopenharmony_ci									\
6862306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(_p_) != 4);					\
6962306a36Sopenharmony_ci									\
7062306a36Sopenharmony_ci	/*								\
7162306a36Sopenharmony_ci	 * spin lock/unlock provide the needed smp_mb() before/after	\
7262306a36Sopenharmony_ci	 */								\
7362306a36Sopenharmony_ci	atomic_ops_lock(__flags);					\
7462306a36Sopenharmony_ci	_prev_ = *_p_;							\
7562306a36Sopenharmony_ci	if (_prev_ == _o_)						\
7662306a36Sopenharmony_ci		*_p_ = _n_;						\
7762306a36Sopenharmony_ci	atomic_ops_unlock(__flags);					\
7862306a36Sopenharmony_ci	_prev_;								\
7962306a36Sopenharmony_ci})
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#endif
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * xchg
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_ci#ifdef CONFIG_ARC_HAS_LLSC
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define __arch_xchg(ptr, val)						\
8962306a36Sopenharmony_ci({									\
9062306a36Sopenharmony_ci	__asm__ __volatile__(						\
9162306a36Sopenharmony_ci	"	ex  %0, [%1]	\n"	/* set new value */	        \
9262306a36Sopenharmony_ci	: "+r"(val)							\
9362306a36Sopenharmony_ci	: "r"(ptr)							\
9462306a36Sopenharmony_ci	: "memory");							\
9562306a36Sopenharmony_ci	_val_;		/* get old value */				\
9662306a36Sopenharmony_ci})
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define arch_xchg_relaxed(ptr, val)					\
9962306a36Sopenharmony_ci({									\
10062306a36Sopenharmony_ci	__typeof__(ptr) _p_ = (ptr);					\
10162306a36Sopenharmony_ci	__typeof__(*(ptr)) _val_ = (val);				\
10262306a36Sopenharmony_ci									\
10362306a36Sopenharmony_ci	switch(sizeof(*(_p_))) {					\
10462306a36Sopenharmony_ci	case 4:								\
10562306a36Sopenharmony_ci		_val_ = __arch_xchg(_p_, _val_);			\
10662306a36Sopenharmony_ci		break;							\
10762306a36Sopenharmony_ci	default:							\
10862306a36Sopenharmony_ci		BUILD_BUG();						\
10962306a36Sopenharmony_ci	}								\
11062306a36Sopenharmony_ci	_val_;								\
11162306a36Sopenharmony_ci})
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#else  /* !CONFIG_ARC_HAS_LLSC */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * EX instructions is baseline and present in !LLSC too. But in this
11762306a36Sopenharmony_ci * regime it still needs use @atomic_ops_lock spinlock to allow interop
11862306a36Sopenharmony_ci * with cmpxchg() which uses spinlock in !LLSC
11962306a36Sopenharmony_ci * (llist.h use xchg and cmpxchg on sama data)
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define arch_xchg(ptr, val)					        \
12362306a36Sopenharmony_ci({									\
12462306a36Sopenharmony_ci	__typeof__(ptr) _p_ = (ptr);					\
12562306a36Sopenharmony_ci	__typeof__(*(ptr)) _val_ = (val);				\
12662306a36Sopenharmony_ci									\
12762306a36Sopenharmony_ci	unsigned long __flags;						\
12862306a36Sopenharmony_ci									\
12962306a36Sopenharmony_ci	atomic_ops_lock(__flags);					\
13062306a36Sopenharmony_ci									\
13162306a36Sopenharmony_ci	__asm__ __volatile__(						\
13262306a36Sopenharmony_ci	"	ex  %0, [%1]	\n"					\
13362306a36Sopenharmony_ci	: "+r"(_val_)							\
13462306a36Sopenharmony_ci	: "r"(_p_)							\
13562306a36Sopenharmony_ci	: "memory");							\
13662306a36Sopenharmony_ci									\
13762306a36Sopenharmony_ci	atomic_ops_unlock(__flags);					\
13862306a36Sopenharmony_ci	_val_;								\
13962306a36Sopenharmony_ci})
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#endif
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#endif
144