/*	$NetBSD: locore_machdep.S,v 1.13 2016/04/03 09:06:28 martin Exp $	*/

/*-
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */
	
#include "opt_kloader.h"

#include <mips/asm.h>
#include <mips/cache_r5900.h>	
#include <mips/cpuregs.h>

#define D_STAT_REG	0xb000e010
#define I_MASK_REG	0xb000f010

	.set	noreorder
	.text
	.align 6	/* align cache line size (64B) */

/*
 * md_imask_update:
 *	this function may be called under EXL=1. don't access KSEG2.
 *	
 * void
 * md_imask_update()
 * {
 * 	register u_int32_t cur_mask, bit;
 * 
 * 	cur_mask = (_reg_read_4(I_MASK_REG) & 0xffff) |
 *	    (_reg_read_4(D_STAT_REG) & 0xffff0000);
 *
 *	bit = (cur_mask ^ ~md_imask) | (cur_mask & md_imask);
 *
 * 	_reg_write_4(I_MASK_REG, bit & __intc_enabled_channel);
 * 	_reg_write_4(D_STAT_REG, bit & D_STAT_CIM(__dmac_enabled_channel));
 * }
 */	 	
LEAF_NOPROFILE(md_imask_update)	
	lw	a0, _C_LABEL(__intc_enabled_channel)
	li	a1, I_MASK_REG
	lw	a3, _C_LABEL(__dmac_enabled_channel)
	li	a2, D_STAT_REG	
	lw	t1, 0(a2)	/* D_STAT */
	lui	t2, 0xffff	
	lw	t0, 0(a1)	/* I_MASK */
	sll	a3, a3, 16
	and	t1, t1, t2
	lw	t3, _C_LABEL(md_imask)
	nor	t2, zero, t2
	and	t0, t0, t2
	or	t0, t0, t1	/* cur_mask */
	nor	ta0, zero, t3
	xor	ta0, ta0, t0	
	and	t3, t3, t0
	or	t0, t3, ta0
	and	t3, t0, a0	/* INTC */
	and	ta0, t0, a3	/* DMAC */
	sw	t3, 0(a1)
	sw	ta0, 0(a2)
	sync.p	
	j	ra
	nop
END(md_imask_update)		

/*	
 * spllowersofthigh:
 *	this function may be called under EXL=1. don't access KSEG2.	
 * void
 * spllowersofthigh()
 * {
 * 	extern void _splnone(void);
 * 
 * 	md_imask = __icu_mask[IPL_SOFTSERIAL];
 * 	md_imask_update(); 
 * 	_splnone();
 * }
 */	
NESTED_NOPROFILE(spllowersofthigh, CALLFRAME_SIZ, ra)
	.mask	0x80000000, -4
	subu	sp, sp, CALLFRAME_SIZ
	sw	ra, CALLFRAME_RA(sp)
	la	a0, _C_LABEL(__icu_mask)
	lw	a0, 16(a0)
	sw	a0, _C_LABEL(md_imask)
	jal	md_imask_update
	nop
	lw	ra, CALLFRAME_RA(sp)	
	li	t0, (MIPS_INT_MASK | MIPS_SR_INT_IE)	
	mtc0	t0, MIPS_COP_0_STATUS
	sync.p
	j	ra
	addu	sp, sp, CALLFRAME_SIZ
END(spllowersofthigh)

#ifdef KLOADER
/*
 * kloader_playstation2_boot(struct kloader_bootinfo *,
 *		struct kloader_page_tag *)	
 *	must be PIC.
 */
LEAF_NOPROFILE(kloader_playstation2_boot)
	lui	t0, 0x1040	# Cu0 | BEV
	mtc0	t0, $12
	sync.p
	/*
	 * 1. load kernel image.
	 */
	move	t6, a1		# p
1:	beqz	t6, 3f
	 move	t7, t6
	lw	t6, 0(t7)	# p = next
	lw	t0, 4(t7)	# src
	lw	ta0, 8(t7)	# dst
	lw	t2, 12(t7)	# sz
	addu	t5, ta0, t2	# dst + sz
2:	lw	t3, 0(t0)	# copy
	sw	t3, 0(ta0)
	addiu	ta0, ta0, 4
	addiu	t0, t0, 4
	bltu	ta0, t5, 2b
	 nop
	b	1b
	 nop
3:	nop

	/* 
	 * 2. Cache flush
	 */
	/*
	 * Flush the data cache.
	 */
	li	t2, CACHE_R5900_SIZE_D
	li	t0, MIPS_KSEG0_START
	srl	t2, 1		# Two way set assoc
	addu	t1, t0, t2	# End address
	sync.l
	sync.p
1:
	// line +0
	cache	CACHEOP_R5900_IWBINV_D,  0(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IWBINV_D,  1(t0);sync.l;sync.p	# way 1
	// line +1
	cache	CACHEOP_R5900_IWBINV_D, 64(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IWBINV_D, 65(t0);sync.l;sync.p	# way 1
	// line +2
	cache	CACHEOP_R5900_IWBINV_D,128(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IWBINV_D,129(t0);sync.l;sync.p	# way 1
	// line +3
	cache	CACHEOP_R5900_IWBINV_D,192(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IWBINV_D,193(t0);sync.l;sync.p	# way 1
	addu	t0, t0, 256	
	bne	t0, t1, 1b
	 nop
	
	/*
	 * Flush the instruction cache.
	 */
	li	t1, CACHE_R5900_SIZE_I
	li	t0, MIPS_KSEG0_START
	srl	t1, 1			# Two way set assoc
	addu	t1, t0, t1		# End address
	sync.l
	sync.p
1:
	#	[12:6]	... line
	#	[0]	... way
	// line +0
	cache	CACHEOP_R5900_IINV_I,  0(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IINV_I,  1(t0);sync.l;sync.p	# way 1
	// line +1	
	cache	CACHEOP_R5900_IINV_I, 64(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IINV_I, 65(t0);sync.l;sync.p	# way 1
	// line +2
	cache	CACHEOP_R5900_IINV_I,128(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IINV_I,129(t0);sync.l;sync.p	# way 1
	// line +3	
	cache	CACHEOP_R5900_IINV_I,192(t0);sync.l;sync.p	# way 0
	cache	CACHEOP_R5900_IINV_I,193(t0);sync.l;sync.p	# way 1
	addu	t0, t0, 256	
	bne	t0, t1, 1b
	 nop

	/* 
	 * 3. jump to kernel entry 
	 */
	lw	a0, 0(a0)
1:	jr	a0
 	 move	sp, a0
	nop
	nop
	nop
	b	1b
	 nop
	/* NOTREACHED */
END(kloader_playstation2_boot)
#endif /* KLOADER */
