/*
* ti omap3530 SoC machine assist
* arm cortex-a8 processor
*
* loader uses R11 as scratch.
* R9 and R10 are used for `extern register' variables.
*
* ARM v7 arch. ref. man. §B1.3.3 that we don't need barriers
* around moves to CPSR.
*/

#include "arm.s"

/*
* MCR and MRC are counter-intuitively named.
*      MCR     coproc, opcode1, Rd, CRn, CRm[, opcode2]        # arm -> coproc
*      MRC     coproc, opcode1, Rd, CRn, CRm[, opcode2]        # coproc -> arm
*/

/*
* Entered here from Das U-Boot or another Plan 9 kernel with MMU disabled.
* Until the MMU is enabled it is OK to call functions provided
* they are within ±32MiB relative and do not require any
* local variables or more than one argument (i.e. there is
* no stack).
*/
TEXT _start(SB), 1, $-4
       MOVW    $setR12(SB), R12                /* load the SB */
       SUB     $KZERO, R12
       ADD     $PHYSDRAM, R12

       /* SVC mode, interrupts disabled */
       MOVW    $(PsrDirq|PsrDfiq|PsrMsvc), R1
       MOVW    R1, CPSR
       BARRIERS

       DELAY(printloopret, 1)
WAVE('\r')
       DELAY(printloopnl, 1)
WAVE('\n')
       /*
        * work around errata
        */
       MRC     CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
       ORR     $(CpACissue1|CpACldstissue1), R1  /* fight omap35x errata 3.1.1.9 */
       ORR     $CpACibe, R1                    /* enable cp15 invalidate */
       ORR     $CpACl1pe, R1                   /* enable l1 parity checking */
       ORR     $CpCalign, R1                   /* catch alignment errors */
       BIC     $CpACasa, R1                    /* no speculative accesses */
       /* go faster with fewer restrictions */
       BIC     $(CpACcachenopipe|CpACcp15serial|CpACcp15waitidle|CpACcp15pipeflush), R1
       MCR     CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
       ISB

       MRC     CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux
       ORR     $CpCl2nowralloc, R1             /* fight cortex errata 460075 */
       ORR     $(CpCl2ecc|CpCl2eccparity), R1
#ifdef TEDIUM
       /*
        * I don't know why this clobbers the system, but I'm tired
        * of arguing with this fussy processor.  To hell with it.
        */
       MCR     CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux
       ISB
#endif
       DELAY(printloops, 1)
WAVE('P')
       /*
        * disable the MMU & caches
        */
       MRC     CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
       BIC     $(CpCdcache|CpCicache|CpCmmu), R1
       ORR     $CpCsbo, R1
       BIC     $CpCsbz, R1
       MCR     CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
       ISB

       MRC     CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
       BIC     $CpACl2en, R1                   /* turn l2 cache off */
       MCR     CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
       ISB

WAVE('l')
       DELAY(printloop3, 1)

WAVE('a')
       /* clear Mach */
       MOVW    $PADDR(MACHADDR), R4            /* address of Mach */
       MOVW    $0, R0
_machZ:
       MOVW    R0, (R4)
       ADD     $4, R4
       CMP.S   $PADDR(L1+L1X(0)), R4   /* end at top-level page table */
       BNE     _machZ

       /*
        * set up the MMU page table
        */

WAVE('n')
       /* clear all PTEs first, to provide a default */
//      MOVW    $PADDR(L1+L1X(0)), R4           /* address of PTE for 0 */
_ptenv0:
       ZEROPTE()
       CMP.S   $PADDR(L1+16*KiB), R4
       BNE     _ptenv0

       DELAY(printloop4, 2)
WAVE(' ')
       /*
        * set up double map of PHYSDRAM, KZERO to PHYSDRAM for first few MBs,
        * but only if KZERO and PHYSDRAM differ.
        */
       MOVW    $PTEDRAM, R2                    /* PTE bits */
       MOVW    $PHYSDRAM, R3                   /* pa */
       CMP     $KZERO, R3
       BEQ     no2map
       MOVW    $PADDR(L1+L1X(PHYSDRAM)), R4  /* address of PTE for PHYSDRAM */
       MOVW    $DOUBLEMAPMBS, R5
_ptdbl:
       FILLPTE()
       SUB.S   $1, R5
       BNE     _ptdbl
no2map:

       /*
        * back up and fill in PTEs for memory at KZERO.
        * beagle has 1 bank of 256MB of SDRAM at PHYSDRAM;
        * igepv2 has 1 bank of 512MB at PHYSDRAM.
        * Map the maximum (512MB).
        */
WAVE('9')
       MOVW    $PTEDRAM, R2                    /* PTE bits */
       MOVW    $PHYSDRAM, R3
       MOVW    $PADDR(L1+L1X(KZERO)), R4       /* start with PTE for KZERO */
       MOVW    $512, R5                        /* inner loop count (MBs) */
_ptekrw:                                        /* set PTEs */
       FILLPTE()
       SUB.S   $1, R5                          /* decrement inner loop count */
       BNE     _ptekrw

       /*
        * back up and fill in PTEs for MMIO
        * stop somewhere after uarts
        */
WAVE(' ')
       MOVW    $PTEIO, R2                      /* PTE bits */
       MOVW    $PHYSIO, R3
       MOVW    $PADDR(L1+L1X(VIRTIO)), R4      /* start with PTE for VIRTIO */
_ptenv2:
       FILLPTE()
       CMP.S   $PADDR(L1+L1X(PHYSIOEND)), R4
       BNE     _ptenv2

       /* mmu.c sets up the trap vectors later */

       /*
        * set up a temporary stack; avoid data & bss segments
        */
       MOVW    $(PHYSDRAM | (128*1024*1024)), R13

       /* invalidate caches */
       BL      cachedinv(SB)
       MOVW    $KZERO, R0
       MCR     CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
       ISB
       MCR     CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
       BARRIERS

WAVE('f')
       /*
        * turn caches on
        */
       MRC     CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
       ORR     $CpACl2en, R1                   /* turn l2 cache on */
       MCR     CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
       BARRIERS

       MRC     CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
       ORR     $(CpCdcache|CpCicache), R1
       MCR     CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
       BARRIERS

WAVE('r')
       /* set the domain access control */
       MOVW    $Client, R0
       BL      dacput(SB)

       DELAY(printloop5, 2)
WAVE('o')
       /* set the translation table base */
       MOVW    $PADDR(L1), R0
       BL      ttbput(SB)

       MOVW    $0, R0
       BL      pidput(SB)              /* paranoia */

WAVE('m')
       /*
        * the little dance to turn the MMU on
        */
       BL      cacheuwbinv(SB)
       BL      mmuinvalidate(SB)
       BL      mmuenable(SB)

WAVE(' ')
       /* warp the PC into the virtual map */
       MOVW    $KZERO, R0
       BL      _r15warp(SB)

       /*
        * now running at KZERO+something!
        */

       MOVW    $setR12(SB), R12                /* reload the SB */

       /*
        * set up temporary stack again, in case we've just switched
        * to a new register set.
        */
       MOVW    $(KZERO|(128*1024*1024)), R13

       /* can now execute arbitrary C code */

       BL      cacheuwbinv(SB)

WAVE('B')
       MOVW    $PHYSDRAM, R3                   /* pa */
       CMP     $KZERO, R3
       BEQ     no2unmap
       /* undo double map of PHYSDRAM, KZERO & first few MBs */
       MOVW    $(L1+L1X(PHYSDRAM)), R4         /* addr. of PTE for PHYSDRAM */
       MOVW    $0, R0
       MOVW    $DOUBLEMAPMBS, R5
_ptudbl:
       ZEROPTE()
       SUB.S   $1, R5
       BNE     _ptudbl
no2unmap:
       BARRIERS
       MOVW    $KZERO, R0
       MCR     CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
       BARRIERS

#ifdef HIGH_SECURITY                            /* i.e., not GP omap */
       /* hack: set `secure monitor' vector base addr for cortex */
//      MOVW    $HVECTORS, R0
       MOVW    $PADDR(L1), R0
       SUB     $(MACHSIZE+(2*1024)), R0
       MCR     CpSC, 0, R0, C(CpVECS), C(CpVECSbase), CpVECSmon
       ISB
#endif

       /*
        * call main in C
        * pass Mach to main and set up the stack in it
        */
       MOVW    $(MACHADDR), R0                 /* Mach */
       MOVW    R0, R13
       ADD     $(MACHSIZE), R13                /* stack pointer */
       SUB     $4, R13                         /* space for link register */
       MOVW    R0, R10                         /* m = MACHADDR */
WAVE('e')
       BL      main(SB)                        /* void main(Mach*) */
       /*FALLTHROUGH*/

/*
* reset the system
*/

TEXT _reset(SB), 1, $-4
       MOVW    $(PsrDirq|PsrDfiq|PsrMsvc), R0
       MOVW    R0, CPSR
       BARRIERS

       DELAY(printloopr, 2)
WAVE('!')
WAVE('r')
WAVE('e')
WAVE('s')
WAVE('e')
WAVE('t')
WAVE('!')
WAVE('\r')
WAVE('\n')

       /* turn the caches off */
       BL      cacheuwbinv(SB)

       MRC     CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
       BIC     $(CpCicache|CpCdcache|CpCalign), R0
       ORR     $CpCsw, R0                      /* enable SWP */
       MCR     CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
       BARRIERS

       /* redo double map of PHYSDRAM, KZERO & first few MBs */
       MOVW    $PTEDRAM, R2                    /* PTE bits */
       MOVW    $PHYSDRAM, R3                   /* pa */
       MOVW    $(L1+L1X(PHYSDRAM)), R4         /* address of PHYSDRAM's PTE */
       MOVW    $DOUBLEMAPMBS, R5
_ptrdbl:
       FILLPTE()
       SUB.S   $1, R5
       BNE     _ptrdbl

       MOVW    $PHYSDRAM, R0
       MCR     CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
       BARRIERS

       /* turn the MMU off */
       MOVW    $PHYSDRAM, R0
       BL      _r15warp(SB)
       BL      mmuinvalidate(SB)
       BL      mmudisable(SB)

       /* set new reset vector */
       MOVW    $HVECTORS, R2
       MOVW    $0xe59ff018, R3                 /* MOVW 0x18(R15), R15 */
       MOVW    R3, (R2)
       BARRIERS

//      MOVW    $PHYSFLASH, R3                  /* TODO */
//      MOVW    R3, 0x20(R2)                    /* where $0xe59ff018 jumps to */

       /* ...and jump to it */
//      MOVW    R2, R15                         /* software reboot */
_limbo:                                         /* should not get here... */
       BL      idlehands(SB)
       B       _limbo                          /* ... and can't get out */
       BL      _div(SB)                        /* hack to load _div, etc. */

TEXT _r15warp(SB), 1, $-4
       BIC     $KSEGM, R14                     /* link reg, will become PC */
       ORR     R0, R14
       BIC     $KSEGM, R13                     /* SP too */
       ORR     R0, R13
       RET

/*
* `single-element' cache operations.
* in arm arch v7, they operate on all cache levels, so separate
* l2 functions are unnecessary.
*/

TEXT cachedwbse(SB), $-4                        /* D writeback SE */
       MOVW    R0, R2

       MOVW    CPSR, R3
       CPSID                                   /* splhi */

       BARRIERS                        /* force outstanding stores to cache */
       MOVW    R2, R0
       MOVW    4(FP), R1
       ADD     R0, R1                          /* R1 is end address */
       BIC     $(CACHELINESZ-1), R0            /* cache line start */
_dwbse:
       MCR     CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEse
       /* can't have a BARRIER here since it zeroes R0 */
       ADD     $CACHELINESZ, R0
       CMP.S   R0, R1
       BGT     _dwbse
       B       _wait

TEXT cachedwbinvse(SB), $-4                     /* D writeback+invalidate SE */
       MOVW    R0, R2

       MOVW    CPSR, R3
       CPSID                                   /* splhi */

       BARRIERS                        /* force outstanding stores to cache */
       MOVW    R2, R0
       MOVW    4(FP), R1
       ADD     R0, R1                          /* R1 is end address */
       BIC     $(CACHELINESZ-1), R0            /* cache line start */
_dwbinvse:
       MCR     CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEse
       /* can't have a BARRIER here since it zeroes R0 */
       ADD     $CACHELINESZ, R0
       CMP.S   R0, R1
       BGT     _dwbinvse
_wait:                                          /* drain write buffer */
       BARRIERS
       /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
       MCR     CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
       ISB

       MOVW    R3, CPSR                        /* splx */
       RET

TEXT cachedinvse(SB), $-4                       /* D invalidate SE */
       MOVW    R0, R2

       MOVW    CPSR, R3
       CPSID                                   /* splhi */

       BARRIERS                        /* force outstanding stores to cache */
       MOVW    R2, R0
       MOVW    4(FP), R1
       ADD     R0, R1                          /* R1 is end address */
       BIC     $(CACHELINESZ-1), R0            /* cache line start */
_dinvse:
       MCR     CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEse
       /* can't have a BARRIER here since it zeroes R0 */
       ADD     $CACHELINESZ, R0
       CMP.S   R0, R1
       BGT     _dinvse
       B       _wait

/*
*  enable mmu and high vectors
*/
TEXT mmuenable(SB), 1, $-4
       MRC     CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
       ORR     $(CpChv|CpCmmu), R0
       MCR     CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
       BARRIERS
       RET

TEXT mmudisable(SB), 1, $-4
       MRC     CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
       BIC     $(CpChv|CpCmmu), R0
       MCR     CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
       BARRIERS
       RET

/*
* If one of these MCR instructions crashes or hangs the machine,
* check your Level 1 page table (at TTB) closely.
*/
TEXT mmuinvalidate(SB), $-4                     /* invalidate all */
       MOVW    CPSR, R2
       CPSID                                   /* interrupts off */

       BARRIERS
       MOVW    PC, R0                          /* some valid virtual address */
       MCR     CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
       BARRIERS
       MOVW    R2, CPSR                        /* interrupts restored */
       RET

TEXT mmuinvalidateaddr(SB), $-4                 /* invalidate single entry */
       MCR     CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinvse
       BARRIERS
       RET

TEXT cpidget(SB), 1, $-4                        /* main ID */
       MRC     CpSC, 0, R0, C(CpID), C(0), CpIDid
       RET

TEXT cpctget(SB), 1, $-4                        /* cache type */
       MRC     CpSC, 0, R0, C(CpID), C(0), CpIDct
       RET

TEXT controlget(SB), 1, $-4                     /* control */
       MRC     CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
       RET

TEXT ttbget(SB), 1, $-4                         /* translation table base */
       MRC     CpSC, 0, R0, C(CpTTB), C(0), CpTTB0
       RET

TEXT ttbput(SB), 1, $-4                         /* translation table base */
       MCR     CpSC, 0, R0, C(CpTTB), C(0), CpTTB0
       MCR     CpSC, 0, R0, C(CpTTB), C(0), CpTTB1     /* cortex has two */
       ISB
       RET

TEXT dacget(SB), 1, $-4                         /* domain access control */
       MRC     CpSC, 0, R0, C(CpDAC), C(0)
       RET

TEXT dacput(SB), 1, $-4                         /* domain access control */
       MCR     CpSC, 0, R0, C(CpDAC), C(0)
       ISB
       RET

TEXT fsrget(SB), 1, $-4                         /* data fault status */
       MRC     CpSC, 0, R0, C(CpFSR), C(0), CpDFSR
       RET

TEXT ifsrget(SB), 1, $-4                        /* instruction fault status */
       MRC     CpSC, 0, R0, C(CpFSR), C(0), CpIFSR
       RET

TEXT farget(SB), 1, $-4                         /* fault address */
       MRC     CpSC, 0, R0, C(CpFAR), C(0x0)
       RET

TEXT getpsr(SB), 1, $-4
       MOVW    CPSR, R0
       RET

TEXT getscr(SB), 1, $-4
       MRC     CpSC, 0, R0, C(CpCONTROL), C(CpCONTROLscr), CpSCRscr
       RET

TEXT pidget(SB), 1, $-4                         /* address translation pid */
       MRC     CpSC, 0, R0, C(CpPID), C(0x0)
       RET

TEXT pidput(SB), 1, $-4                         /* address translation pid */
       MCR     CpSC, 0, R0, C(CpPID), C(0x0)
       ISB
       RET

TEXT splhi(SB), 1, $-4
       MOVW    CPSR, R0
       CPSID                                   /* turn off interrupts */

       MOVW    $(MACHADDR+4), R2               /* save caller pc in Mach */
       MOVW    R14, 0(R2)
       RET

TEXT spllo(SB), 1, $-4                  /* start marker for devkprof.c */
       MOVW    CPSR, R0
       CPSIE
       RET

TEXT splx(SB), 1, $-4
       MOVW    $(MACHADDR+0x04), R2            /* save caller pc in Mach */
       MOVW    R14, 0(R2)

       MOVW    CPSR, R3
       MOVW    R0, CPSR                        /* reset interrupt level */
       MOVW    R3, R0                          /* must return old CPSR */
       RET

TEXT spldone(SB), 1, $0                         /* end marker for devkprof.c */
       RET

TEXT islo(SB), 1, $-4
       MOVW    CPSR, R0
       AND     $(PsrDirq), R0
       EOR     $(PsrDirq), R0
       RET

TEXT    tas(SB), $-4
TEXT    _tas(SB), $-4
       MOVW    R0,R1
       MOVW    $1,R0
       SWPW    R0,(R1)                 /* fix: deprecated in armv7 */
       RET

TEXT clz(SB), $-4
       CLZ(0, 0)                       /* 0 is R0 */
       RET

TEXT setlabel(SB), 1, $-4
       MOVW    R13, 0(R0)              /* sp */
       MOVW    R14, 4(R0)              /* pc */
       MOVW    $0, R0
       RET

TEXT gotolabel(SB), 1, $-4
       MOVW    0(R0), R13              /* sp */
       MOVW    4(R0), R14              /* pc */
       MOVW    $1, R0
       RET

TEXT getcallerpc(SB), 1, $-4
       MOVW    0(R13), R0
       RET

TEXT idlehands(SB), $-4
       BARRIERS
       WFI
       RET

TEXT coherence(SB), $-4
       BARRIERS
       RET

#include "cache.v7.s"