162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef __ASM_OPENRISC_FUTEX_H
362306a36Sopenharmony_ci#define __ASM_OPENRISC_FUTEX_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#ifdef __KERNEL__
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/futex.h>
862306a36Sopenharmony_ci#include <linux/uaccess.h>
962306a36Sopenharmony_ci#include <asm/errno.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
1262306a36Sopenharmony_ci({								\
1362306a36Sopenharmony_ci	__asm__ __volatile__ (					\
1462306a36Sopenharmony_ci		"1:	l.lwa	%0, %2			\n"	\
1562306a36Sopenharmony_ci			insn				"\n"	\
1662306a36Sopenharmony_ci		"2:	l.swa	%2, %1			\n"	\
1762306a36Sopenharmony_ci		"	l.bnf	1b			\n"	\
1862306a36Sopenharmony_ci		"	 l.ori	%1, r0, 0		\n"	\
1962306a36Sopenharmony_ci		"3:					\n"	\
2062306a36Sopenharmony_ci		".section .fixup,\"ax\"			\n"	\
2162306a36Sopenharmony_ci		"4:	l.j	3b			\n"	\
2262306a36Sopenharmony_ci		"	 l.addi	%1, r0, %3		\n"	\
2362306a36Sopenharmony_ci		".previous				\n"	\
2462306a36Sopenharmony_ci		".section __ex_table,\"a\"		\n"	\
2562306a36Sopenharmony_ci		".word	1b,4b,2b,4b			\n"	\
2662306a36Sopenharmony_ci		".previous				\n"	\
2762306a36Sopenharmony_ci		: "=&r" (oldval), "=&r" (ret), "+m" (*uaddr)	\
2862306a36Sopenharmony_ci		: "i" (-EFAULT), "r" (oparg)			\
2962306a36Sopenharmony_ci		: "cc", "memory"				\
3062306a36Sopenharmony_ci		);						\
3162306a36Sopenharmony_ci})
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline int
3462306a36Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int oldval = 0, ret;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
3962306a36Sopenharmony_ci		return -EFAULT;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	switch (op) {
4262306a36Sopenharmony_ci	case FUTEX_OP_SET:
4362306a36Sopenharmony_ci		__futex_atomic_op("l.or %1,%4,%4", ret, oldval, uaddr, oparg);
4462306a36Sopenharmony_ci		break;
4562306a36Sopenharmony_ci	case FUTEX_OP_ADD:
4662306a36Sopenharmony_ci		__futex_atomic_op("l.add %1,%0,%4", ret, oldval, uaddr, oparg);
4762306a36Sopenharmony_ci		break;
4862306a36Sopenharmony_ci	case FUTEX_OP_OR:
4962306a36Sopenharmony_ci		__futex_atomic_op("l.or %1,%0,%4", ret, oldval, uaddr, oparg);
5062306a36Sopenharmony_ci		break;
5162306a36Sopenharmony_ci	case FUTEX_OP_ANDN:
5262306a36Sopenharmony_ci		__futex_atomic_op("l.and %1,%0,%4", ret, oldval, uaddr, ~oparg);
5362306a36Sopenharmony_ci		break;
5462306a36Sopenharmony_ci	case FUTEX_OP_XOR:
5562306a36Sopenharmony_ci		__futex_atomic_op("l.xor %1,%0,%4", ret, oldval, uaddr, oparg);
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	default:
5862306a36Sopenharmony_ci		ret = -ENOSYS;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!ret)
6262306a36Sopenharmony_ci		*oval = oldval;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return ret;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic inline int
6862306a36Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
6962306a36Sopenharmony_ci			      u32 oldval, u32 newval)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	int ret = 0;
7262306a36Sopenharmony_ci	u32 prev;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
7562306a36Sopenharmony_ci		return -EFAULT;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	__asm__ __volatile__ (				\
7862306a36Sopenharmony_ci		"1:	l.lwa	%1, %2		\n"	\
7962306a36Sopenharmony_ci		"	l.sfeq	%1, %3		\n"	\
8062306a36Sopenharmony_ci		"	l.bnf	3f		\n"	\
8162306a36Sopenharmony_ci		"	 l.nop			\n"	\
8262306a36Sopenharmony_ci		"2:	l.swa	%2, %4		\n"	\
8362306a36Sopenharmony_ci		"	l.bnf	1b		\n"	\
8462306a36Sopenharmony_ci		"	 l.nop			\n"	\
8562306a36Sopenharmony_ci		"3:				\n"	\
8662306a36Sopenharmony_ci		".section .fixup,\"ax\"		\n"	\
8762306a36Sopenharmony_ci		"4:	l.j	3b		\n"	\
8862306a36Sopenharmony_ci		"	 l.addi	%0, r0, %5	\n"	\
8962306a36Sopenharmony_ci		".previous			\n"	\
9062306a36Sopenharmony_ci		".section __ex_table,\"a\"	\n"	\
9162306a36Sopenharmony_ci		".word	1b,4b,2b,4b		\n"	\
9262306a36Sopenharmony_ci		".previous			\n"	\
9362306a36Sopenharmony_ci		: "+r" (ret), "=&r" (prev), "+m" (*uaddr) \
9462306a36Sopenharmony_ci		: "r" (oldval), "r" (newval), "i" (-EFAULT) \
9562306a36Sopenharmony_ci		: "cc",	"memory"			\
9662306a36Sopenharmony_ci		);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	*uval = prev;
9962306a36Sopenharmony_ci	return ret;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#endif /* __KERNEL__ */
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#endif /* __ASM_OPENRISC_FUTEX_H */
105