/**
 ** Standalone startup code for Linux PROM emulator.
 ** Copyright 1999 Pete A. Zaitcev
 ** This code is licensed under GNU General Public License.
 **/
/*
 * $Id: head.S,v 1.12 2002/07/23 05:47:09 zaitcev Exp $
 */

#define __ASSEMBLY__
#include <asm/asi.h>
#include "pstate.h"
#include "lsu.h"
#include "cpustate.h"
#define NO_QEMU_PROTOS
#define NO_OPENBIOS_PROTOS
#include "arch/common/fw_cfg.h"

#define PROM_ADDR 0x1fff0000000
#define CFG_ADDR  0x1fe02000510
#define HZ        1 * 1000 * 1000
#define TICK_INT_DIS 0x8000000000000000

        .globl	entry, _entry

	.section ".text", "ax"
	.align	8
        .register %g2, #scratch
        .register %g3, #scratch
        .register %g6, #scratch
        .register %g7, #scratch

/*
 * Entry point
 * We start execution from here.
 */
_entry:
entry:
        ! Set up CPU state
        wrpr    %g0, PSTATE_PRIV, %pstate
        wr      %g0, 0, %fprs
        wrpr    %g0, 0x0, %tl

        ! Extract NWINDOWS from %ver
        rdpr    %ver, %g1
        and     %g1, 0xf, %g1
        dec     %g1
        wrpr    %g1, 0, %cleanwin
        wrpr    %g1, 0, %cansave
        wrpr    %g0, 0, %canrestore
        wrpr    %g0, 0, %otherwin
        wrpr    %g0, 0, %wstate
        ! disable timer now
        setx    TICK_INT_DIS, %g2, %g1
        wr      %g1, 0, %tick_cmpr

        ! Disable I/D MMUs and caches
        stxa    %g0, [%g0] ASI_LSU_CONTROL

        ! Check signature "QEMU"
        setx    CFG_ADDR, %g2, %g5
        mov     FW_CFG_SIGNATURE, %g2
        stha    %g2, [%g5] ASI_PHYS_BYPASS_EC_E_L
        inc     %g5
        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2
        cmp     %g2, 'Q'
        bne     bad_conf
         nop
        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2
        cmp     %g2, 'E'
        bne     bad_conf
         nop
        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2
        cmp     %g2, 'M'
        bne     bad_conf
         nop
        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2
        cmp     %g2, 'U'
        bne     bad_conf
         nop

        ! Clear ITLB
        mov     6 << 3, %g1
        stxa    %g0, [%g1] ASI_IMMU
        stxa    %g0, [%g1] ASI_DMMU
        mov     63 << 3, %g1
1:      stxa    %g0, [%g1] ASI_ITLB_DATA_ACCESS
        subcc   %g1, 1 << 3, %g1
        bpos    1b
         nop

        ! Clear DTLB
        mov     63 << 3, %g1
1:      stxa    %g0, [%g1] ASI_DTLB_DATA_ACCESS
        subcc   %g1, 1 << 3, %g1
        bpos    1b
         nop

        ! Get memory size from configuration device
        ! NB: little endian format
        mov     FW_CFG_RAM_SIZE, %g2
        dec     %g5
        stha    %g2, [%g5] ASI_PHYS_BYPASS_EC_E_L
        inc     %g5
        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g4

        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3
        sllx    %g3, 8, %g3
        or      %g3, %g4, %g4

        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3
        sllx    %g3, 16, %g3
        or      %g3, %g4, %g4

        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3
        sllx    %g3, 24, %g3
        or      %g3, %g4, %g4

        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3
        sllx    %g3, 32, %g3
        or      %g3, %g4, %g4

        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3
        sllx    %g3, 40, %g3
        or      %g3, %g4, %g4

        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3
        sllx    %g3, 48, %g3
        or      %g3, %g4, %g4

        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3
        sllx    %g3, 56, %g3
        or      %g3, %g4, %g1
        ! %g1 contains end of memory

        setx    _end, %g7, %g3
        set     0x7ffff, %g2
        add     %g3, %g2, %g3
        andn    %g3, %g2, %g3
        setx    _data, %g7, %g2
        sub     %g3, %g2, %g2
        sub	%g1, %g2, %g2			! %g2 = start of private memory
        mov     %g2, %l0

	! setup .data & .bss
        setx    _data, %g7, %g4
        sub     %g3, %g4, %g5
        srlx    %g5, 19, %g6			! %g6 = # of 512k .bss pages
	set	0xc0000000, %g3
	sllx	%g3, 32, %g3
	or	%g3, 0x7e, %g3
	! valid, 512k, locked, cacheable(I/E/C), priv, writable
	set	48, %g7
1:	stxa	%g4, [%g7] ASI_DMMU		! vaddr = _data + N * 0x80000, ctx=0
	or	%g2, %g3, %g5
	! paddr = start_mem + N * 0x80000
	stxa	%g5, [%g0] ASI_DTLB_DATA_IN
	set	0x80000, %g5
	add	%g2, %g5, %g2
	add	%g4, %g5, %g4
	deccc	%g6
	bne	1b
	 nop

	! setup .rodata, also make .text readable
	setx	_data, %g7, %g5
	setx	_start, %g7, %g4
        sub     %g5, %g4, %g5
	srlx    %g5, 19, %g6			! %g6 = # of 512k .rodata pages
	set	48, %g7
	set	0x80000, %g5
        setx    PROM_ADDR, %l1, %l2
1:      stxa	%g4, [%g7] ASI_DMMU		! vaddr = _rodata, ctx=0
	set	0xc0000000, %g3
	sllx	%g3, 32, %g3
	or	%g3, 0x7c, %g3
	or	%l2, %g3, %g3
	! valid, 512k, locked, cacheable(I/E/C), priv
	! paddr = _rodata + N * 0x10000
	stxa	%g3, [%g0] ASI_DTLB_DATA_IN
	add	%g4, %g5, %g4
	deccc	%g6
	bne	1b
	 add	%l2, %g5, %l2

	membar	#Sync

	setx	_start, %g7, %g4
	setx	_rodata, %g7, %g5
        sub     %g5, %g4, %g5
	set 0x7ffff, %g7
	add %g5, %g7, %g5					! round to 512k
        srlx    %g5, 19, %g6			! %g6 = # of 512k .text pages
	set	0x80000, %g5
	set	48, %g7
        setx    PROM_ADDR, %l1, %l2
1:      stxa	%g4, [%g7] ASI_IMMU		! vaddr = _start, ctx=0
	set	0xc0000000, %g3
	sllx	%g3, 32, %g3
	or	%g3, 0x7c, %g3
	or	%l2, %g3, %g3
	! valid, 512k, locked, cacheable(I/E/C), priv
	! paddr = _start + N * 0x80000
	stxa	%g3, [%g0] ASI_ITLB_DATA_IN
	add	%g4, %g5, %g4
	deccc	%g6
	bne	1b
	 add	%l2, %g5, %l2

	flush	%g4

	mov	%g1, %g3

	set     8, %g2
	sta     %g0, [%g2] ASI_DMMU		! set primary ctx=0

	! Enable I/D MMUs and caches
        setx    lowmem, %g2, %g1
	set	LSU_CONTROL_DM|LSU_CONTROL_IM|LSU_CONTROL_DC|LSU_CONTROL_IC, %g2
        jmp     %g1
         stxa	%g2, [%g0] ASI_LSU_CONTROL

lowmem:
        /* Copy the DATA section from ROM. */
        setx	_data - 8, %o7, %o0             ! First address of DATA
        setx	_bss, %o7, %o1                  ! Last address of DATA
        setx    _start, %o7, %o2
        sub     %o0, %o2, %o2                   ! _data - _start
        setx    PROM_ADDR, %o7, %o3
        add     %o3, %o2, %o2                   ! PROM_ADDR + (_data - _start)
        ba	2f
         nop
1:
        ldxa    [%o2] ASI_PHYS_BYPASS_EC_E, %g1
        stx	%g1, [%o0]
2:
        add	%o2, 0x8, %o2
        subcc	%o0, %o1, %g0
        bl	1b
         add	%o0, 0x8, %o0

        /* Zero out our BSS section. */
        setx    _bss - 8, %o7, %o0              ! First address of BSS
        setx    _end - 8, %o7, %o1                  ! Last address of BSS
        ba	2f
         nop
1:
        stx     %g0, [%o0]
2:
        subcc	%o0, %o1, %g0
        bl	1b
         add	%o0, 0x8, %o0

        setx    trap_table, %g2, %g1
        wrpr    %g1, %tba

        setx    qemu_mem_size, %g7, %g1
        stx     %g3, [%g1]

        setx    _data, %g7, %g1                 ! Store va->pa conversion factor
        sub     %g1, %l0, %g2
        setx    va_shift, %g7, %g1
        stx     %g2, [%g1]

        /* Finally, turn on traps so that we can call c-code. */
	wrpr    %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate

        /* Set up a default context */
        setx    __context, %g2, %g1
        ldx     [%g1], %g1
        
        SAVE_CPU_STATE(entry)
        
        /* Set up local stack pointer */
        setx    _estack - 2047, %o2, %sp
        
        /* And for the main context */
        add	%sp, - 192 - 0x500, %g2 
        stx	%g2, [%g1 + 0xa0]

        ! 100 Hz timer
        setx    TICK_INT_DIS, %g2, %g1
        rd      %tick, %g2
        andn    %g2, %g1, %g2
        set     HZ, %g1
        add     %g1, %g2, %g1
        wr      %g1, 0, %tick_cmpr

        /* Switch to our main context.
         * Main context is statically defined in C.
         */

        call    __switch_context
         nop

        /* We get here when the main context switches back to
         * the boot context.
         */
bad_conf:
        b       bad_conf
         nop
