/*	$OpenBSD: atomic.S,v 1.6 2014/07/15 16:26:28 miod Exp $	*/

/*
 * Copyright (c) 2009 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <machine/asm.h>

#ifdef M88110
#define	CACHE_LINE	32
#else
#define	CACHE_LINE	16
#endif
	.data

/*
 * A __cpu_simple_lock_t used to provide the inter-processor interlock,
 * alone on its cache line.
 */
	.balign	CACHE_LINE
ASLOCAL(__atomic_interlock)
	.word	0
	.balign	CACHE_LINE

	.text

/*
 * Register usage in this file:
 *
 * r2 data address
 * r3 bits to set or clear
 * r4 argument / scratch
 * r5 return address
 * r6 interlock address
 * r7 psr upon entry
 * r8 active psr
 * r9 scratch
 */

ENTRY(atomic_setbits_int)
	or	%r5, %r1, %r0		/* save return address */
	bsr	_C_LABEL(__atomic_lock)

	ld	%r4, %r2, %r0
	or	%r4, %r4, %r3
	st	%r4, %r2, %r0

	br	_C_LABEL(__atomic_unlock)

ENTRY(atomic_clearbits_int)
	or	%r5, %r1, %r0		/* save return address */
	bsr	_C_LABEL(__atomic_lock)

	ld	%r4, %r2, %r0
	or	%r4, %r4, %r3
	xor	%r4, %r4, %r3		/* r4 &= ~r3 */
	st	%r4, %r2, %r0

	br	_C_LABEL(__atomic_unlock)

ENTRY(atomic_add_int_nv_mp)
	or	%r5, %r1, %r0		/* save return address */
	bsr	_C_LABEL(__atomic_lock)

	or	%r9, %r2, %r0
	ld	%r2, %r9, %r0
	addu	%r2, %r2, %r3
	st	%r2, %r9, %r0

	br	_C_LABEL(__atomic_unlock)

ENTRY(atomic_sub_int_nv_mp)
	or	%r5, %r1, %r0		/* save return address */
	bsr	_C_LABEL(__atomic_lock)

	or	%r9, %r2, %r0
	ld	%r2, %r9, %r0
	subu	%r2, %r2, %r3
	st	%r2, %r9, %r0

	br	_C_LABEL(__atomic_unlock)

ENTRY(atomic_cas_uint_mp)
	or	%r5, %r1, %r0		/* save return address */
	bsr	_C_LABEL(__atomic_lock)

	ld	%r9, %r2, %r0
	cmp	%r3, %r3, %r9
	bb0	eq,  %r3, 1f
	st	%r4, %r2, %r0
1:
	or	%r2, %r9, %r0

	br	_C_LABEL(__atomic_unlock)

ENTRY(atomic_swap_uint_mp)
	or	%r5, %r1, %r0		/* save return address */
	bsr	_C_LABEL(__atomic_lock)

	ld	%r4, %r2, %r0
	st	%r3, %r2, %r0
	or	%r2, %r4, %r0

	br	_C_LABEL(__atomic_unlock)

GLOBAL(__atomic_lock)

/*
 * If running a kernel with support for both 88100 and 88110 compiled-in
 * on a 88100 machine, the 88100 code (shorter) will be copied over in
 * vector_init().
 */

#ifdef M88110
ASLOCAL(__atomic_lock_88110)
	/*
	 * This is the 88110 version: disable shadowing and interrupts,
	 * then grab the interlock.
	 */

	or.u	%r6, %r0, %hi16(_ASM_LABEL(__atomic_interlock))
	or	%r6, %r6, %lo16(_ASM_LABEL(__atomic_interlock))

	ldcr	%r7, PSR
	set	%r8, %r7, 1<PSR_INTERRUPT_DISABLE_BIT>
	set	%r8, %r8, 1<PSR_SHADOW_FREEZE_BIT>
	stcr	%r8, PSR
	FLUSH_PIPELINE
1:
	or	%r9, %r0, 1	/* __SIMPLELOCK_LOCKED */
	xmem	%r9, %r6, %r0
	bcnd	eq0, %r9, 3f
2:
	ld	%r9, %r6, %r0
	bcnd	eq0, %r9, 1b
	br	2b
3:
	jmp	%r1
#endif

#ifdef M88100
GLOBAL(__atomic_lock_88100)
	/*
	 * This is the 88100 version: disable interrupts, then grab
	 * the interlock.
	 */

	or.u	%r6, %r0, %hi16(_ASM_LABEL(__atomic_interlock))
	or	%r6, %r6, %lo16(_ASM_LABEL(__atomic_interlock))

	ldcr	%r7, PSR
	set	%r8, %r7, 1<PSR_INTERRUPT_DISABLE_BIT>
	stcr	%r8, PSR
	FLUSH_PIPELINE

1:
	or	%r9, %r0, 1	/* __SIMPLELOCK_LOCKED */
	xmem	%r9, %r6, %r0
	bcnd	eq0, %r9, 3f
2:
	ld	%r9, %r6, %r0
	bcnd	eq0, %r9, 1b
	br	2b
3:
	jmp	%r1
GLOBAL(__atomic_lock_88100_end)
#endif

GLOBAL(__atomic_unlock)

/*
 * If running a kernel with support for both 88100 and 88110 compiled-in
 * on a 88100 machine, the 88100 code (shorter) will be copied over in
 * vector_init().
 */

#ifdef M88110
ASLOCAL(__atomic_unlock_88110)
	/*
	 * This is the 88110 version: release the interlock, set up
	 * exception registers to return to our caller with initial
	 * psr restored.
	 */

	st	%r0, %r6, %r0	/* release interlock */

	stcr	%r5, EXIP	/* return address */
	stcr	%r7, EPSR	/* original PSR */

	/*
	 * No need to workaround errata #18 (see m88110_user_rte in
	 * eh_common.S), as we are not returning to user mode.
	 */
	RTE
#endif

#ifdef M88100
GLOBAL(__atomic_unlock_88100)
	/*
	 * This is the 88100 version: release the interlock,
	 * restore psr and return to the caller.
	 */

	st	%r0, %r6, %r0	/* release interlock */

	stcr	%r7, PSR
	FLUSH_PIPELINE
	
	jmp	%r5
GLOBAL(__atomic_unlock_88100_end)
#endif
