#ifndef _ASM_M32R_ASSEMBLER_H
#define _ASM_M32R_ASSEMBLER_H

/*
 * linux/asm-m32r/assembler.h
 *
 * Copyright (C) 2004  Hirokazu Takata <takata at linux-m32r.org>
 *
 * This file contains M32R architecture specific macro definitions.
 */

#include <linux/config.h>

#ifndef __STR
#ifdef __ASSEMBLY__
#define __STR(x) x
#else
#define __STR(x) #x
#endif
#endif /* __STR */

#ifdef CONFIG_SMP
#define M32R_LOCK	__STR(lock)
#define M32R_UNLOCK	__STR(unlock)
#else
#define M32R_LOCK	__STR(ld)
#define M32R_UNLOCK	__STR(st)
#endif

#ifdef __ASSEMBLY__
#undef ENTRY
#define ENTRY(name) ENTRY_M name
	.macro  ENTRY_M name
	.global \name
	ALIGN
\name:
	.endm
#endif


/**
 * LDIMM - load immediate value
 * STI - enable interruption
 * CLI - disable interruption
 */

#ifdef __ASSEMBLY__

#define LDIMM(reg,x) LDIMM reg x
	.macro LDIMM reg x
	seth	\reg, #high(\x)
	or3	\reg, \reg, #low(\x)
	.endm

#if !(defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_M32104))
#define STI(reg) STI_M reg
	.macro STI_M reg
	setpsw  #0x40	    ->	nop
	; WORKAROUND: "-> nop" is a workaround for the M32700(TS1).
	.endm

#define CLI(reg) CLI_M reg
	.macro CLI_M reg
	clrpsw  #0x40	    ->	nop
	; WORKAROUND: "-> nop" is a workaround for the M32700(TS1).
	.endm
#else	/* CONFIG_CHIP_M32102 || CONFIG_CHIP_M32104 */
#define STI(reg) STI_M reg
	.macro STI_M reg
	mvfc	\reg, psw
	or3	\reg, \reg, #0x0040
	mvtc	\reg, psw
	.endm

#define CLI(reg) CLI_M reg
	.macro CLI_M reg
	mvfc	\reg, psw
	and3	\reg, \reg, #0xffbf
	mvtc	\reg, psw
	.endm
#endif	/* CONFIG_CHIP_M32102 */

	.macro	SAVE_ALL
	push	r0		; orig_r0
	push	sp		; spi (r15)
	push	lr		; r14
	push	r13
	mvfc	r13, cr3	; spu
	push	r13
	mvfc	r13, bbpc
	push	r13
	mvfc	r13, bbpsw
	push	r13
	mvfc	r13, bpc
	push	r13
	mvfc	r13, psw
	push	r13
#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2)
	mvfaclo	r13, a1
	push	r13
	mvfachi r13, a1
	push	r13
	mvfaclo	r13, a0
	push	r13
	mvfachi	r13, a0
	push	r13
#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R)
	mvfaclo	r13
	push	r13
	mvfachi	r13
	push	r13
#else
#error unknown isa configuration
#endif
	ldi	r13, #-1
	push	r13		; syscall_nr (default: -1)
	push	r12
	push	r11
	push	r10
	push	r9
	push	r8
	push	r7
	push	r3
	push	r2
	push	r1
	push	r0
	addi	sp, #-4		; room for implicit pt_regs parameter
	push	r6
	push	r5
	push	r4
	.endm

	.macro	RESTORE_ALL
	pop	r4
	pop	r5
	pop	r6
	addi	sp, #4
	pop	r0
	pop	r1
	pop	r2
	pop	r3
	pop	r7
	pop	r8
	pop	r9
	pop	r10
	pop	r11
	pop	r12
	addi	r15, #4		; Skip syscall number
#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2)
	pop	r13
	mvtachi	r13, a0
	pop	r13
	mvtaclo	r13, a0
	pop	r13
	mvtachi	r13, a1
	pop	r13
	mvtaclo	r13, a1
#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R)
	pop	r13
	mvtachi	r13
	pop	r13
	mvtaclo	r13
#else
#error unknown isa configuration
#endif
	pop	r14
	mvtc	r14, psw
	pop	r14
	mvtc	r14, bpc
	addi	sp, #8		; Skip bbpsw, bbpc
	pop	r14
	mvtc	r14, cr3	; spu
	pop	r13
	pop	lr		; r14
	pop	sp		; spi (r15)
	addi	sp, #4		; Skip orig_r0
	.fillinsn
1:	rte
	.section .fixup,"ax"
2:	bl	do_exit
	.previous
	.section __ex_table,"a"
	ALIGN
	.long	1b, 2b
	.previous
	.endm

#define GET_CURRENT(reg)  get_current reg
	.macro get_current reg
	ldi  \reg, #-8192
	and  \reg, sp
	.endm

#if !(defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_M32104))
	.macro	SWITCH_TO_KERNEL_STACK
	; switch to kernel stack (spi)
	clrpsw	#0x80	    ->	nop
	.endm
#else	/* CONFIG_CHIP_M32102 || CONFIG_CHIP_M32104 */
	.macro	SWITCH_TO_KERNEL_STACK
	push	r0		; save r0 for working
	mvfc	r0, psw
	and3	r0, r0, #0x00ff7f
	mvtc	r0, psw
	slli	r0, #16
	bltz	r0, 1f		; check BSM-bit
;
	;; called from kernel context: previous stack = spi
	pop	r0		; retrieve r0
	bra	2f
	.fillinsn
1:
	;; called from user context: previous stack = spu
	mvfc	r0, cr3		; spu
	addi	r0, #4
	mvtc	r0, cr3		; spu
	ld	r0, @(-4,r0)	; retrieve r0
	.fillinsn
2:
	.endm
#endif	/* CONFIG_CHIP_M32102 || CONFIG_CHIP_M32104 */

#endif	/* __ASSEMBLY__ */

#endif	/* _ASM_M32R_ASSEMBLER_H */
