18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * xchg/cmpxchg operations for the Hexagon architecture
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#ifndef _ASM_CMPXCHG_H
98c2ecf20Sopenharmony_ci#define _ASM_CMPXCHG_H
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * __xchg - atomically exchange a register and a memory location
138c2ecf20Sopenharmony_ci * @x: value to swap
148c2ecf20Sopenharmony_ci * @ptr: pointer to memory
158c2ecf20Sopenharmony_ci * @size:  size of the value
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Only 4 bytes supported currently.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Note:  there was an errata for V2 about .new's and memw_locked.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistatic inline unsigned long __xchg(unsigned long x, volatile void *ptr,
238c2ecf20Sopenharmony_ci				   int size)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	unsigned long retval;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	/*  Can't seem to use printk or panic here, so just stop  */
288c2ecf20Sopenharmony_ci	if (size != 4) do { asm volatile("brkpt;\n"); } while (1);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
318c2ecf20Sopenharmony_ci	"1:	%0 = memw_locked(%1);\n"    /*  load into retval */
328c2ecf20Sopenharmony_ci	"	memw_locked(%1,P0) = %2;\n" /*  store into memory */
338c2ecf20Sopenharmony_ci	"	if (!P0) jump 1b;\n"
348c2ecf20Sopenharmony_ci	: "=&r" (retval)
358c2ecf20Sopenharmony_ci	: "r" (ptr), "r" (x)
368c2ecf20Sopenharmony_ci	: "memory", "p0"
378c2ecf20Sopenharmony_ci	);
388c2ecf20Sopenharmony_ci	return retval;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * Atomically swap the contents of a register with memory.  Should be atomic
438c2ecf20Sopenharmony_ci * between multiple CPU's and within interrupts on the same CPU.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci#define xchg(ptr, v) ((__typeof__(*(ptr)))__xchg((unsigned long)(v), (ptr), \
468c2ecf20Sopenharmony_ci	sizeof(*(ptr))))
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci *  see rt-mutex-design.txt; cmpxchg supposedly checks if *ptr == A and swaps.
508c2ecf20Sopenharmony_ci *  looks just like atomic_cmpxchg on our arch currently with a bunch of
518c2ecf20Sopenharmony_ci *  variable casting.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define cmpxchg(ptr, old, new)					\
558c2ecf20Sopenharmony_ci({								\
568c2ecf20Sopenharmony_ci	__typeof__(ptr) __ptr = (ptr);				\
578c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __old = (old);			\
588c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __new = (new);			\
598c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __oldval = 0;			\
608c2ecf20Sopenharmony_ci								\
618c2ecf20Sopenharmony_ci	asm volatile(						\
628c2ecf20Sopenharmony_ci		"1:	%0 = memw_locked(%1);\n"		\
638c2ecf20Sopenharmony_ci		"	{ P0 = cmp.eq(%0,%2);\n"		\
648c2ecf20Sopenharmony_ci		"	  if (!P0.new) jump:nt 2f; }\n"		\
658c2ecf20Sopenharmony_ci		"	memw_locked(%1,p0) = %3;\n"		\
668c2ecf20Sopenharmony_ci		"	if (!P0) jump 1b;\n"			\
678c2ecf20Sopenharmony_ci		"2:\n"						\
688c2ecf20Sopenharmony_ci		: "=&r" (__oldval)				\
698c2ecf20Sopenharmony_ci		: "r" (__ptr), "r" (__old), "r" (__new)		\
708c2ecf20Sopenharmony_ci		: "memory", "p0"				\
718c2ecf20Sopenharmony_ci	);							\
728c2ecf20Sopenharmony_ci	__oldval;						\
738c2ecf20Sopenharmony_ci})
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#endif /* _ASM_CMPXCHG_H */
76