/*-
 * Copyright (c) 2011 Oleksandr Tymoshenko
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD$
 */

#include <machine/asm.h>
#include <machine/cpuregs.h>
#include <machine/octeon_cop2.h>

#include "assym.s"

.set noreorder

#define SAVE_COP2_REGISTER(reg)				\
	dmfc2	t1, reg; sd	t1, reg##_OFFSET(a0)


#define RESTORE_COP2_REGISTER(reg)			\
	ld	t1, reg##_OFFSET(a0); dmtc2	t1, reg##_SET
 
LEAF(octeon_cop2_save)

	/* save original cop2 status in t2*/
	mfc0	t2, MIPS_COP_0_STATUS
	or	t0, t2, MIPS_SR_COP_2_BIT
	and	t0, t0, ~MIPS_SR_INT_IE
	mtc0	t0, MIPS_COP_0_STATUS

	/* Get CvmCtl register */
	dmfc0	t0, $9, 7

	/* CRC state */
	SAVE_COP2_REGISTER(COP2_CRC_IV)
	SAVE_COP2_REGISTER(COP2_CRC_LENGTH)
	SAVE_COP2_REGISTER(COP2_CRC_POLY)

	/* if CvmCtl[NODFA_CP2] -> save_nodfa */
	bbit1	t0, 28, save_nodfa
	nop

	/* LLM state */
	SAVE_COP2_REGISTER(COP2_LLM_DAT0)
	SAVE_COP2_REGISTER(COP2_LLM_DAT1)

save_nodfa:
	/* crypto stuff is irrelevant if CvmCtl[NOCRYPTO]  */
	bbit1	t0, 26, save_done
	nop

	SAVE_COP2_REGISTER(COP2_3DES_IV)
	SAVE_COP2_REGISTER(COP2_3DES_KEY0)
	SAVE_COP2_REGISTER(COP2_3DES_KEY1)
	SAVE_COP2_REGISTER(COP2_3DES_KEY2)
	SAVE_COP2_REGISTER(COP2_3DES_RESULT)

	SAVE_COP2_REGISTER(COP2_AES_INP0)
	SAVE_COP2_REGISTER(COP2_AES_IV0)
	SAVE_COP2_REGISTER(COP2_AES_IV1)
	SAVE_COP2_REGISTER(COP2_AES_KEY0)
	SAVE_COP2_REGISTER(COP2_AES_KEY1)
	SAVE_COP2_REGISTER(COP2_AES_KEY2)
	SAVE_COP2_REGISTER(COP2_AES_KEY3)
	SAVE_COP2_REGISTER(COP2_AES_KEYLEN)
	SAVE_COP2_REGISTER(COP2_AES_RESULT0)
	SAVE_COP2_REGISTER(COP2_AES_RESULT1)

	dmfc0	t0, $15
	li	t1, 0x000d0000 /* Octeon Pass1 */
	beq	t0, t1, save_pass1
	nop

	SAVE_COP2_REGISTER(COP2_HSH_DATW0)
	SAVE_COP2_REGISTER(COP2_HSH_DATW1)
	SAVE_COP2_REGISTER(COP2_HSH_DATW2)
	SAVE_COP2_REGISTER(COP2_HSH_DATW3)
	SAVE_COP2_REGISTER(COP2_HSH_DATW4)
	SAVE_COP2_REGISTER(COP2_HSH_DATW5)
	SAVE_COP2_REGISTER(COP2_HSH_DATW6)
	SAVE_COP2_REGISTER(COP2_HSH_DATW7)
	SAVE_COP2_REGISTER(COP2_HSH_DATW8)
	SAVE_COP2_REGISTER(COP2_HSH_DATW9)
	SAVE_COP2_REGISTER(COP2_HSH_DATW10)
	SAVE_COP2_REGISTER(COP2_HSH_DATW11)
	SAVE_COP2_REGISTER(COP2_HSH_DATW12)
	SAVE_COP2_REGISTER(COP2_HSH_DATW13)
	SAVE_COP2_REGISTER(COP2_HSH_DATW14)
	SAVE_COP2_REGISTER(COP2_HSH_IVW0)
	SAVE_COP2_REGISTER(COP2_HSH_IVW1)
	SAVE_COP2_REGISTER(COP2_HSH_IVW2)
	SAVE_COP2_REGISTER(COP2_HSH_IVW3)
	SAVE_COP2_REGISTER(COP2_HSH_IVW4)
	SAVE_COP2_REGISTER(COP2_HSH_IVW5)
	SAVE_COP2_REGISTER(COP2_HSH_IVW6)
	SAVE_COP2_REGISTER(COP2_HSH_IVW7)
	SAVE_COP2_REGISTER(COP2_GFM_MULT0)
	SAVE_COP2_REGISTER(COP2_GFM_MULT1)
	SAVE_COP2_REGISTER(COP2_GFM_POLY)
	SAVE_COP2_REGISTER(COP2_GFM_RESULT0)
	SAVE_COP2_REGISTER(COP2_GFM_RESULT1)
	/* restore saved COP2 status */
	mtc0	t2, MIPS_COP_0_STATUS
	jr ra
	nop

save_pass1:
	SAVE_COP2_REGISTER(COP2_HSH_DATW0_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_DATW1_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_DATW2_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_DATW3_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_DATW4_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_DATW5_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_DATW6_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_IVW0_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_IVW1_PASS1)
	SAVE_COP2_REGISTER(COP2_HSH_IVW2_PASS1)

save_done:	
	/* restore saved COP2 status */
	mtc0	t2, MIPS_COP_0_STATUS
	jr ra
	nop
END(octeon_cop2_save)

LEAF(octeon_cop2_restore)
	/* save original cop2 status in t2*/
	mfc0	t2, MIPS_COP_0_STATUS
	or	t0, t2, MIPS_SR_COP_2_BIT
	and	t0, t0, ~MIPS_SR_INT_IE
	mtc0	t0, MIPS_COP_0_STATUS
	/* Get CvmCtl register */
	dmfc0	t0, $9, 7

	/* CRC state */
	RESTORE_COP2_REGISTER(COP2_CRC_IV)
	RESTORE_COP2_REGISTER(COP2_CRC_LENGTH)
	RESTORE_COP2_REGISTER(COP2_CRC_POLY)

	/* if CvmCtl[NODFA_CP2] -> save_nodfa */
	bbit1	t0, 28, restore_nodfa
	nop

	/* LLM state */
	RESTORE_COP2_REGISTER(COP2_LLM_DAT0)
	RESTORE_COP2_REGISTER(COP2_LLM_DAT1)

restore_nodfa:
	/* crypto stuff is irrelevant if CvmCtl[NOCRYPTO]  */
	bbit1	t0, 26, restore_done
	nop

	RESTORE_COP2_REGISTER(COP2_3DES_IV)
	RESTORE_COP2_REGISTER(COP2_3DES_KEY0)
	RESTORE_COP2_REGISTER(COP2_3DES_KEY1)
	RESTORE_COP2_REGISTER(COP2_3DES_KEY2)
	RESTORE_COP2_REGISTER(COP2_3DES_RESULT)

	RESTORE_COP2_REGISTER(COP2_AES_INP0)
	RESTORE_COP2_REGISTER(COP2_AES_IV0)
	RESTORE_COP2_REGISTER(COP2_AES_IV1)
	RESTORE_COP2_REGISTER(COP2_AES_KEY0)
	RESTORE_COP2_REGISTER(COP2_AES_KEY1)
	RESTORE_COP2_REGISTER(COP2_AES_KEY2)
	RESTORE_COP2_REGISTER(COP2_AES_KEY3)
	RESTORE_COP2_REGISTER(COP2_AES_KEYLEN)
	RESTORE_COP2_REGISTER(COP2_AES_RESULT0)
	RESTORE_COP2_REGISTER(COP2_AES_RESULT1)

	dmfc0	t0, $15
	li	t1, 0x000d0000 /* Octeon Pass1 */
	beq	t0, t1, restore_pass1
	nop

	RESTORE_COP2_REGISTER(COP2_HSH_DATW0)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW1)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW2)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW3)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW4)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW5)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW6)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW7)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW8)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW9)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW10)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW11)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW12)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW13)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW14)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW0)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW1)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW2)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW3)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW4)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW5)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW6)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW7)
	RESTORE_COP2_REGISTER(COP2_GFM_MULT0)
	RESTORE_COP2_REGISTER(COP2_GFM_MULT1)
	RESTORE_COP2_REGISTER(COP2_GFM_POLY)
	RESTORE_COP2_REGISTER(COP2_GFM_RESULT0)
	RESTORE_COP2_REGISTER(COP2_GFM_RESULT1)
	/* restore saved COP2 status */
	mtc0	t2, MIPS_COP_0_STATUS
	jr ra
	nop

restore_pass1:
	RESTORE_COP2_REGISTER(COP2_HSH_DATW0_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW1_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW2_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW3_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW4_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW5_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_DATW6_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW0_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW1_PASS1)
	RESTORE_COP2_REGISTER(COP2_HSH_IVW2_PASS1)

restore_done:	
	/* restore saved COP2 status */
	mtc0	t2, MIPS_COP_0_STATUS
	jr ra
	nop
END(octeon_cop2_restore)
