18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci// Copyright (C) 2005-2017 Andes Technology Corporation
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#ifndef __NDS32_FUTEX_H__
58c2ecf20Sopenharmony_ci#define __NDS32_FUTEX_H__
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/futex.h>
88c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
98c2ecf20Sopenharmony_ci#include <asm/errno.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define __futex_atomic_ex_table(err_reg)			\
128c2ecf20Sopenharmony_ci	"	.pushsection __ex_table,\"a\"\n"		\
138c2ecf20Sopenharmony_ci	"	.align	3\n"					\
148c2ecf20Sopenharmony_ci	"	.long	1b, 4f\n"				\
158c2ecf20Sopenharmony_ci	"	.long	2b, 4f\n"				\
168c2ecf20Sopenharmony_ci	"	.popsection\n"					\
178c2ecf20Sopenharmony_ci	"	.pushsection .fixup,\"ax\"\n"			\
188c2ecf20Sopenharmony_ci	"4:	move	%0, " err_reg "\n"			\
198c2ecf20Sopenharmony_ci	"	b	3b\n"					\
208c2ecf20Sopenharmony_ci	"	.popsection"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg)	\
238c2ecf20Sopenharmony_ci	smp_mb();						\
248c2ecf20Sopenharmony_ci	asm volatile(					\
258c2ecf20Sopenharmony_ci	"	movi	$ta, #0\n"				\
268c2ecf20Sopenharmony_ci	"1:	llw	%1, [%2+$ta]\n"				\
278c2ecf20Sopenharmony_ci	"	" insn "\n"					\
288c2ecf20Sopenharmony_ci	"2:	scw	%0, [%2+$ta]\n"				\
298c2ecf20Sopenharmony_ci	"	beqz	%0, 1b\n"				\
308c2ecf20Sopenharmony_ci	"	movi	%0, #0\n"				\
318c2ecf20Sopenharmony_ci	"3:\n"							\
328c2ecf20Sopenharmony_ci	__futex_atomic_ex_table("%4")				\
338c2ecf20Sopenharmony_ci	: "=&r" (ret), "=&r" (oldval)				\
348c2ecf20Sopenharmony_ci	: "r" (uaddr), "r" (oparg), "i" (-EFAULT)		\
358c2ecf20Sopenharmony_ci	: "cc", "memory")
368c2ecf20Sopenharmony_cistatic inline int
378c2ecf20Sopenharmony_cifutex_atomic_cmpxchg_inatomic(u32 * uval, u32 __user * uaddr,
388c2ecf20Sopenharmony_ci			      u32 oldval, u32 newval)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int ret = 0;
418c2ecf20Sopenharmony_ci	u32 val, tmp, flags;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
448c2ecf20Sopenharmony_ci		return -EFAULT;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	smp_mb();
478c2ecf20Sopenharmony_ci	asm volatile ("       movi    $ta, #0\n"
488c2ecf20Sopenharmony_ci		      "1:     llw     %1, [%6 + $ta]\n"
498c2ecf20Sopenharmony_ci		      "       sub     %3, %1, %4\n"
508c2ecf20Sopenharmony_ci		      "       cmovz   %2, %5, %3\n"
518c2ecf20Sopenharmony_ci		      "       cmovn   %2, %1, %3\n"
528c2ecf20Sopenharmony_ci		      "2:     scw     %2, [%6 + $ta]\n"
538c2ecf20Sopenharmony_ci		      "       beqz    %2, 1b\n"
548c2ecf20Sopenharmony_ci		      "3:\n                   " __futex_atomic_ex_table("%7")
558c2ecf20Sopenharmony_ci		      :"+&r"(ret), "=&r"(val), "=&r"(tmp), "=&r"(flags)
568c2ecf20Sopenharmony_ci		      :"r"(oldval), "r"(newval), "r"(uaddr), "i"(-EFAULT)
578c2ecf20Sopenharmony_ci		      :"$ta", "memory");
588c2ecf20Sopenharmony_ci	smp_mb();
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	*uval = val;
618c2ecf20Sopenharmony_ci	return ret;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic inline int
658c2ecf20Sopenharmony_ciarch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	int oldval = 0, ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!access_ok(uaddr, sizeof(u32)))
708c2ecf20Sopenharmony_ci		return -EFAULT;
718c2ecf20Sopenharmony_ci	switch (op) {
728c2ecf20Sopenharmony_ci	case FUTEX_OP_SET:
738c2ecf20Sopenharmony_ci		__futex_atomic_op("move	%0, %3", ret, oldval, tmp, uaddr,
748c2ecf20Sopenharmony_ci				  oparg);
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci	case FUTEX_OP_ADD:
778c2ecf20Sopenharmony_ci		__futex_atomic_op("add	%0, %1, %3", ret, oldval, tmp, uaddr,
788c2ecf20Sopenharmony_ci				  oparg);
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci	case FUTEX_OP_OR:
818c2ecf20Sopenharmony_ci		__futex_atomic_op("or	%0, %1, %3", ret, oldval, tmp, uaddr,
828c2ecf20Sopenharmony_ci				  oparg);
838c2ecf20Sopenharmony_ci		break;
848c2ecf20Sopenharmony_ci	case FUTEX_OP_ANDN:
858c2ecf20Sopenharmony_ci		__futex_atomic_op("and	%0, %1, %3", ret, oldval, tmp, uaddr,
868c2ecf20Sopenharmony_ci				  ~oparg);
878c2ecf20Sopenharmony_ci		break;
888c2ecf20Sopenharmony_ci	case FUTEX_OP_XOR:
898c2ecf20Sopenharmony_ci		__futex_atomic_op("xor	%0, %1, %3", ret, oldval, tmp, uaddr,
908c2ecf20Sopenharmony_ci				  oparg);
918c2ecf20Sopenharmony_ci		break;
928c2ecf20Sopenharmony_ci	default:
938c2ecf20Sopenharmony_ci		ret = -ENOSYS;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!ret)
978c2ecf20Sopenharmony_ci		*oval = oldval;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return ret;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci#endif /* __NDS32_FUTEX_H__ */
102