/* $NetBSD: entry.S,v 1.6 2012/01/14 20:03:12 phx Exp $ */

#include <powerpc/psl.h>
#include <powerpc/spr.h>
#include <powerpc/oea/spr.h>
#include <powerpc/oea/bat.h>
#include <powerpc/oea/hid.h>

	.text
	.globl _start
_start:
	/*
	 * Save possible argc and argv values from the firmware, usually
	 * passed in r3 and r4.
	 * When started with "bootm", as a Linux kernel module, r6 and r7
	 * point to the start and end address of the bootargs.
	 */
	mr	30,3
	mr	31,4
	mr	28,6
	mr	29,7

	/* Disable interrupts and everything except the MMU. */
	mfmsr	3
	andi.	3,3,PSL_DR|PSL_IR
	mtmsr	3
	isync

	/*
	 * U-Boot/PPCBoot forgets to flush the cache when using the "bootm"
	 * command, so we have to do that now.
	 */
	lis	11,_start@ha
	addi	11,11,_start@l
	li	10,-32
	and	11,11,10
	lis	12,(_edata+31)@ha
	addi	12,12,(_edata+31)@l
	bl	syncicache

	mfspr	11,SPR_HID0
	andi.	0,11,HID0_DCE
	ori	11,11,HID0_ICE
	ori	8,11,HID0_ICFI
	bne	1f			/* don't invalidate the D-cache */
	ori	8,8,HID0_DCFI		/* unless it wasn't enabled */
1:
	mfmsr	0
	andi.	0,0,PSL_DR
	beq	2f
	lis	5,0xfec00000@ha		/* CONFIG_ADDR of PCI */
	lis	6,0xfee00000@ha		/* CONFIG_DATA of PCI */
	mfspr	3,SPR_DBAT0U
	mfspr	4,SPR_DBAT0L
	bl	dbat_sanity_check
	beq	3f
	mfspr	3,SPR_DBAT1U
	mfspr	4,SPR_DBAT1L
	bl	dbat_sanity_check
	beq	3f
	mfspr	3,SPR_DBAT2U
	mfspr	4,SPR_DBAT2L
	bl	dbat_sanity_check
	beq	3f
	mfspr	3,SPR_DBAT3U
	mfspr	4,SPR_DBAT3L
	bl	dbat_sanity_check
	beq	3f

2:	/* Disable D-cache */
	li	0,HID0_DCE
	andc	11,11,0
	b	4f

3:	/* Enable D-cache */
	ori	11,11,HID0_DCE

4:
	lis	1,BAT123@ha
	addi	1,1,BAT123@l
	lwz	3,0(1)
	lwz	4,4(1)
	mtdbatl	1,3
	mtdbatu	1,4
	lwz	3,8(1)
	lwz	4,12(1)
	mtdbatl	2,3
	mtdbatu	2,4
	lwz	3,16(1)
	lwz	4,20(1)
	mtdbatl	3,3
	mtdbatu	3,4

	sync
	mtspr	SPR_HID0,8		/* enable and invalidate caches */
	sync
	mtspr	SPR_HID0,11		/* enable caches */
	sync
	isync

	/* make sure .bss gets zeroed. */
	li	0,0
	lis	8,edata@ha
	addi	8,8,edata@l
	lis	9,end@ha
	addi	9,9,end@l
5:	cmpw	0,8,9			/* edata & end are >= word aligned */
	bge	6f
	stw	0,0(8)
	addi	8,8,4
	b	5b

6:
	/* prepare stack at +1MB from _start, 16-byte aligned */
	lis	1,_start@ha
	addi	1,1,_start@l
	addis	1,1,0x100000@ha
	li	10,-16
	and	1,1,10
	stw	0,0(1)

	bl	brdsetup
#ifdef DEBUG
	bl	init_vectors
#endif
	mr	3,30
	mr	4,31
	mr	5,28
	mr	6,29
	bl	main

hang:	b	hang
	/* NOTREACHED */

dbat_sanity_check:
	andi.	0,3,BAT_Vs
	beq	2f
	andi.	0,4,BAT_I|BAT_PP_RW
	cmpwi	0,0,BAT_I|BAT_PP_RW
	bnelr
	rlwinm	0,3,15,4,14
	andis.	3,3,0xfffe0000@ha	/* BAT_EPI */
	andis.	4,4,BAT_RPN@ha
	cmplw	0,3,4
	bnelr
	add	4,4,0
	oris	4,4,0x0001ffff@ha
	ori	4,4,0x0001ffff@l
	cmplw	0,3,5
	bgt	1f
	cmplw	0,5,4
	bgt	1f
	li	5,0
1:	cmplw	0,3,6
	bgt	2f
	cmplw	0,6,4
	bgt	2f
	li	6,0
2:	cmplw	0,5,6
	blr

/*
 * run(startsym, endsym, howto, bootinfo, entry)
 */
	.globl	run
run:
	mtctr	7			/* hat trick jump to entry point */
	bctr

/*
 * newaltboot(argc, argv, altboot_base, altboot_len)
 * To be executed in a safe memory region. Copies the new altboot from
 * altboot_base to 0x1000000 and starts it there.
 */
	.globl	newaltboot
newaltboot:
	lis	7,0x1000000@h
	mr	11,7
	subi	7,7,4
	subi	5,5,4
	add	12,11,6
	addi	6,6,3
	srawi	6,6,2
	mtctr	6
1:	lwzu	8,4(5)
	stwu	8,4(7)
	bdnz+	1b
	mtctr	11
	addi	12,12,31
	bl	syncicache
	bctr
syncicache:
/* r11=start, r12=end, r10=scratch */
	mr	10,11
2:	dcbst	0,10
	addi	10,10,32
	cmplw	10,12
	ble	2b
	sync
3:	icbi	0,11
	addi	11,11,32
	cmplw	11,12
	ble	3b
	sync
	isync
	blr
	.globl	newaltboot_end
newaltboot_end:


/* 8-bit i/o access */
	.globl	out8
out8:
	stb	4,0(3)
	eieio
	blr

	.globl	in8
in8:
	lbz	3,0(3)
	eieio
	blr

/*
 * reverse endian access to mimic outw/outl/inw/inl
 */
	.globl	out16rb
	.globl	iohtole16
out16rb:
iohtole16:
	sthbrx	4,0,3
	eieio
	blr 

	.globl	out32rb
	.globl	iohtole32
out32rb:
iohtole32:
	stwbrx	4,0,3
	eieio
	blr

	.globl	in16rb
	.globl	iole16toh
in16rb:
iole16toh:
	lhbrx	3,0,3
	eieio
	blr

	.globl	in32rb
	.globl	iole32toh
in32rb:
iole32toh:
	lwbrx	3,0,3
	eieio
	blr

#ifdef DEBUG
	/*
	 * Call an exception handler, which prints out all information
	 * about the type of exception, cpu registers, stack frame
	 * backtrace, etc.
	 * Use a new stack at 0x2000 and make room for 32 GPRs, and 15
	 * special registers. The layout will be:
	 * 0x00: link area
	 * 0x10: R0
	 * ...
	 * 0x8c: R31
	 * 0x90: CR, XER, LR, CTR
	 * 0xa0: SRR0, SRR1, DAR, DSISR
	 * 0xb0: DMISS, DCMP, HASH1, HASH2
	 * 0xc0: IMISS, ICMP, RPA
	 *
	 */
	.globl	trap
trap:
	mtsprg1	1
	mfmsr	1
	andis.	1,1,PSL_TGPR@h
	beq	1f
	andi.	1,1,0xffff		/* make sure TGPR is disabled */
	mtmsr	1
	isync
	mtsprg1	1			/* and save the real r1 again */
1:	li	1,0x2000-16-(32*4+15*4)
	stmw	2,24(1)			/* save r2..r31 */
	stw	0,16(1)			/* save r0 */
	mfsprg1	3
	stw	3,20(1)			/* and finally r1 */
	mfcr	3
	stw	3,0x90(1)
	mfxer	3
	stw	3,0x94(1)
	mflr	3
	stw	3,0x98(1)
	mfctr	3
	stw	3,0x9c(1)
	mfsrr0	3
	stw	3,0xa0(1)
	mfsrr1	3
	stw	3,0xa4(1)
	mfdar	3
	stw	3,0xa8(1)
	mfdsisr	3
	stw	3,0xac(1)
	mfspr	3,976
	stw	3,0xb0(1)
	mfspr	3,977
	stw	3,0xb4(1)
	mfspr	3,978
	stw	3,0xb8(1)
	mfspr	3,979
	stw	3,0xbc(1)
	mfspr	3,980
	stw	3,0xc0(1)
	mfspr	3,981
	stw	3,0xc4(1)
	mfspr	3,982
	stw	3,0xc8(1)
	bl	call_handler
call_handler:
	lis	11,exception_handler@ha
	addi	11,11,exception_handler@l
	mtsrr0	11
	li	0,PSL_DR|PSL_IR
	mtsrr1	0
	mflr	3
	subi	3,3,call_handler-trap
	addi	4,1,16
	rfi
	.globl	trap_end
trap_end:
#endif

	.data
#define	xBATL(pa, wimg, pp)						\
	((pa) | (wimg) | (pp))
#define	xBATU(va, len, v)						\
	((va) | ((len) & BAT_BL) | ((v) & BAT_V))
BAT123:
	.long xBATL(0x80000000, BAT_I|BAT_G, BAT_PP_RW)
	.long xBATU(0x80000000, BAT_BL_256M, BAT_Vs)
	.long xBATL(0xfc000000, BAT_I|BAT_G, BAT_PP_RW)
	.long xBATU(0xfc000000, BAT_BL_64M, BAT_Vs)
	.long xBATL(0x70000000, BAT_I|BAT_G, BAT_PP_RW)
	.long xBATU(0x70000000, BAT_BL_128K, BAT_Vs)
