/* A fake 'bios' which does nothing but move a kernel image 
 * to RAM address zero and then starts that...
 */

#include <bsp/residual.h>

#define LD_CACHE_LINE_SIZE  5
#define INIT_STACK (0x100 - 16) /* 16-byte/svr4 aligned */

/* These offsets must correspond to declaration in qemu_fakeres.c */
#define DAT_LEN    0
#define RES_OFF    4
#define CMD_OFF    8
#define CMD_LEN   12
#define IMG_ADR   16

/* Non-volatile registers */
#define OBASE     30
#define PCID      25
#define PCIA      26

#define PCI_MAX_DEV 32

#define BA_OPCODE(tgt)  ((18<<(31-5)) | 2 | ((tgt) & 0x03fffffc))

	.global fake_data
	.global res_set_memsz

	.global _start
_start:
	lis    1,  INIT_STACK@h
	ori    1,1,INIT_STACK@l

		/* qemu 0.14.1 has the wrong exception prefix for 74xx CPUs
		 * (bug 811683). Work around this by putting a stub at 0x00000X00
		 * which simply jumps to high memory. We only need the SC exception
		 * for now.
		 */
	lis  3,    BA_OPCODE(0xfff00000)@h
	ori  3, 3, BA_OPCODE(0xfff00000)@l
	li   4, 0x0c00
	add  3, 3, 4
	stw  3, 0(4)
	dcbf 0, 4
	icbi 0, 4

	bl     pci_irq_set
		/* copy residual to RAM and fix up; 
		 * this routine returns a pointer to
		 * a 'fake_data' struct. If reading
		 * NVRAM failed then the return value
		 * points to a fall-back version in
		 * ROM...
		 */
	bl     res_copy
		/* fake_data pointer to R29 */
	mr     29, 3

		/* Load up R3..R5 with PreP mandated 
		 * values (R3: residual, R4: kernel image,
		 * R5: OpenFirmware PTR (or NULL).
		 */

		/* load R3 with residual pointer  */
	lwz    3, RES_OFF(29)
	add    3, 3, 29

		/* load R4 with image address     */
	lwz    4, IMG_ADR(29)

		/* load R5 with zero (OFW = NULL) */
	li     5, 0
		/* EXTENSION: R6 = cmdline start  */
	lwz    6, CMD_OFF(29)
	add    6, 6, 29
		/* EXTENSION: R7 = cmdline end    */
	lwz    7, CMD_LEN(29)
	add    7, 7, 6

		/* jump to image address          */
	mtctr  4
	bctr

	.org 0x100
	b    _start

	.org 0x110
template:
	mfsrr0 30
	mfsrr1 31
1:  b      1b
template_end:

	.org 0xc00
	b    monitor
	

	.org 0x4000
codemove: /* src/dst are cache-aligned */
	addi   5,5,(1<<LD_CACHE_LINE_SIZE)-1
	srwi   5,5,LD_CACHE_LINE_SIZE
	addi   3,3,-4
	addi   4,4,-4
1:
	li     0,  (1<<LD_CACHE_LINE_SIZE)
	mtctr  0
2:
	lwzu   0,  4(3)
	stwu   0,  4(4)
	bdnz   2b
	dcbf   0,4
	icbi   0,4
	addic. 5,5,-1
	bne  1b
	blr

cpexc:
	lis    3,template@h
	ori    3,3,template@l
	li     5,template_end-template
	b      codemove

monitor:
	stwu   1,-16(1)
	stw    OBASE, 8(1)
	lis    OBASE, 0x80000000@h
	cmplwi 10,0x63 /* enter_monitor -> RESET */
	bne    10f
hwreset:
	li   3,1
	stb  3,0x92(OBASE)
1:  b 1b
10: cmplwi 10,0x1d /* .NETCTRL -> ignore */
	bne    10f
	b      ret_from_mon
10: b hwreset      /* unknown -> RESET */ 

ret_from_mon:
	lwz    OBASE,8(1)
	lwz    1,0(1)
	rfi

rcb:
	stwbrx 3, 0, PCIA
	lbzx   3, 0, PCID
	blr

wcb:
	stwbrx 3, 0, PCIA
	stbx   4, 0, PCID
	blr

rcd:
	stwbrx 3, 0, PCIA
	lwbrx  3, 0, PCID
	blr

/* fixup pci interrupt line register according to what
 * qemu does: line = ((pin-1) +  slot_no) & 1 ? 11 : 9;
 */
pci_irq_set:
		/* set up stack frame */
	stwu    1, -32(1)
	mflr    0
	stw     0,  32+4(1)
		/* load counter with # of PCI devs */	
	li      0, PCI_MAX_DEV
	mtctr   0
		/* save non-volatile registers we use
		 * in stack frame
		 */
	stw    20,               8(1)
	stw  PCIA,              12(1)
	stw  PCID,              16(1)
		/* load non-volatile registers with
		 * intended values.
		 */
	lis  20,         0x80000000@h /* key for slot # 0             */
	lis  PCIA,       0x80000cf8@h /* PCI config space address reg */
	ori  PCIA, PCIA, 0x80000cf8@l 
	addi PCID, PCIA, 4            /* PCI config space data    reg */

		/* loop over all slots and fix up PCI IRQ LINE */
1:
	mr   3, 20
	bl   rcd
	addi 3, 3, 1
	cmplwi 3, 0      /* slot empty (= -1 + 1 = 0) ? */
	beq  2f
	addi 3, 20, 0x3d
	bl   rcb
	cmplwi 3, 0
	beq  2f
	slwi  4, 3, 11
	addi  3, 20, 0x3c
	xor   4, 4, 3    /* bit 11 = slot # + irq_num [zero-based] + 1 */
	andi. 4, 4, 0x0800
	li   4, 11
	beq  3f
	li   4,  9
3:
	bl   wcb
2:
	addi 20, 20, 0x0800 /* next slot */
    bdnz 1b

		/* restore and return */
	lwz 20,  32+4(1)
	mtlr 20
	lwz PCID, 16(1)
	lwz PCIA, 12(1)
	lwz 20,    8(1)
	lwz 1,     0(1)
	blr

	.section .romentry, "ax"
	b     _start
