/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id: crt0.S 18542 2008-09-17 19:06:26Z nls $
 *
 * Copyright (C) 2008 by Karl Kurbjun
 *
 * Arm bootloader and startup code based on startup.s from the iPodLinux loader
 *  Copyright (c) 2003, Daniel Palffy (dpalffy (at) rainstorm.org)
 *  Copyright (c) 2005, Bernard Leach <leachbj@bouncycastle.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "config.h"
#include "cpu.h"

/* Exception Handlers */
.section .vectors,"ax",%progbits
.code 32

.global vectors
vectors:
    b   start
    b   undef_instr_handler
    b   software_int_handler
    b   prefetch_abort_handler
    b   data_abort_handler
    b   reserved_handler
    b   irq_handler
    b   fiq_handler

/* This branch is used to make sure that we know where the shutdown routine
 *  is located in flash (0x04000020)
 */
    b   rom_shutdown

/* Add some strings to detect the bootloader in flash and give it a version
 * number.  (0x04000024, 0x04000028)
 */
.string "ROCKBOX"
.word   0x0001

/*
 * Function: word_copy
 *  Variables:
 *      r0 = from
 *      r1 = to
 *      r2 = length
 */

.section    .init.text, "ax", %progbits
.align      0x04
.global     word_copy
.type       word_copy, %function
word_copy: 
    subs    r2, r2, #0x04
    ldrge   r3, [r0], #4
    strge   r3, [r1], #4
    bgt     word_copy
    bx lr
.ltorg  
.size       word_copy, .-word_copy

/*
 * Entry: start
 *  Variables:
 *      none
 */

.section    .init.text,"ax",%progbits
.code       32
.align      0x04 /* Align */
.global     start
start:
    msr     cpsr, #0xd3 /* enter supervisor mode, disable IRQ */

    /* Disable the watchdog */
    ldr     r2, =0x00000000
    mov     r1, #0x53000000
    str     r2, [r1]

    /* Mask all Interupts to be safe */
    ldr     r2, =0xFFFFFFFF
    mov     r1, #0x4A000000
    str     r2, [r1, #0x08]

    /* Submask too */
    ldr     r2, =0x00003FFF
    str     r2, [r1, #0x1C]

    /* Check if loaded by the old bootloader or by the OF.  This copy routine
     * cannot run/copy properly until the memory has been initialized, so the copy
     * routine later is still necessary.  The old bootloader/OF will initialize the
     * memory.
     * Be careful with code size above this as well since this routine has to start
     * before 0x100 for it to work right.
     */

    /* Get the execute address (cannot be past 0x100 for this to work */
    ldr     r0, =0xffffff00
    and     r0, pc, r0

    /* Calculate the length of the code needed to run/copy */
    ldr     r1, = _vectorstart
    ldr     r2, = _initdata_end
    sub     r2, r2, r1

    add     r3, r2, #0x30000000

    /* Is there enough space to copy without overwriting? */
    cmp     r0, r3

    /* There's enough space, skip copying */
    bgt     skipreset

    /* Is this code running from 0x0?  If so skip copy. */
    cmplt   r0, #0
    beq     skipreset
    
    /* There's not enough space to copy without overwriting, copy to safe spot
     * and reset
     */
    mov     r1, #0x31000000     /* copy location */
    bl      word_copy

    mov     pc, #0x31000000

    skipreset:  

    /* Initial Clock Setup */
    /* set Bus to Asynchronous mode (full speed) */    
    mov     r0, #0
    mrc     p15, 0, r0, c1, c0, 0
    ldr     r1,  =0xC0000000
    orr     r0, r0, r1 
    mcr     p15, 0, r0, c1, c0, 0

    mov     r2, #0x7
    mov     r1, #0x4C000000
    str     r2, [r1, #0x14]

    mov     r2, #0x0
    str     r2, [r1, #0x18]

    ldr     r2, =0xFFFFFFFF
    str     r2, [r1]

    ldr     r2, =0x0003C042
    str     r2, [r1, #0x08]

    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop

    ldr     r2, =0x000C9042
    str     r2, [r1, #0x04]

    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop

    /* Setup MISCCR */
    ldr     r2, =0x00613020
    mov     r1, #0x56000000
    str     r2, [r1, #0x80]

    /* Memory setup (taken from 0x5070) */

    /* BWSCON
     *      Reserved 0
     * Bank 0:
     *      Bus width 01 (16 bit)
     * Bank 1:
     *      Buswidth 00 (8 bit)
     *      Disable wait 0
     *      Not using UB/LB 0
     * Bank 2:
     *      Buswidth 10 (32 bit)
     *      Disable wait 0
     *      Not using UB/LB 0
     * Bank 3:
     *      Buswidth 10 (32 bit)
     *      Disable wait 0
     *      Use UB/LB 1
     * Bank 4:
     *      Buswidth 10 (32 bit)
     *      Disable wait 0
     *      Use UB/LB 1
     * Bank 5: 
     *      Buswidth 00 (8 bit)
     *      Disable wait 0
     *      Not using UB/LB 0
     * Bank 6:
     *      Buswidth 10 (32 bit)
     *      Disable wait 0
     *      Not using UB/LB 0
     * Bank 7:
     *      Buswidth 00 (8 bit)
     *      Disable wait 0
     *      Not using UB/LB 0
     */
    ldr     r2, =0x01055102
    mov     r1, #0x48000000
    str     r2, [r1]

    /* BANKCON0 
     *      Pagemode: normal (1 data) 00
     *      Pagemode access cycle: 2 clocks 00
     *      Address hold: 2 clocks 10
     *      Chip selection hold time: 1 clock 10
     *      Access cycle: 8 clocks 101
     *      Chip select setup time: 1 clock 01
     *      Address setup time: 0 clock 00
     */
    ldr     r2, =0x00000D60
    str     r2, [r1, #0x04]


    /* BANKCON1 
     *      Pagemode: normal (1 data) 00
     *      Pagemode access cycle: 2 clocks 00
     *      Address hold: 0 clocks 00
     *      Chip selection hold time: 0 clock 00
     *      Access cycle: 1 clocks 000
     *      Chip select setup time: 0 clocks 00
     *      Address setup time: 0 clocks 00
     */
    ldr     r2, =0x00000000
    str     r2, [r1, #0x08]

    /* BANKCON2 
     *      Pagemode: normal (1 data) 00
     *      Pagemode access cycle: 2 clocks 00
     *      Address hold: 2 clocks 10
     *      Chip selection hold time: 2 clocks 10
     *      Access cycle: 14 clocks 111
     *      Chip select setup time: 4 clocks 11
     *      Address setup time: 0 clocks 00
     */
    ldr     r2, =0x00001FA0
    str     r2, [r1, #0xC]

    /* BANKCON3 */
    ldr     r2, =0x00001D80
    str     r2, [r1, #0x10]
    /* BANKCON4 */
    str     r2, [r1, #0x14]

    /* BANKCON5 */
    ldr     r2, =0x00000000
    str     r2, [r1, #0x18]

    /* BANKCON6/7
     *      SCAN:   9 bit       01
     *      Trcd:   3 clocks    01
     *      Tcah:   0 clock     00
     *      Tcoh:   0 clock     00
     *      Tacc:   1 clock     000
     *      Tcos:   0 clock     00
     *      Tacs:   0 clock     00
     *      MT:     Sync DRAM   11
     */
    ldr     r2, =0x00018005
    str     r2, [r1, #0x1C]
    /* BANKCON7 */
    str     r2, [r1, #0x20]

    /* REFRESH */
    ldr     r2, =0x00980501
    str     r2, [r1, #0x24]

    /* BANKSIZE
     *      BK76MAP: 32M/32M    000
     *      Reserved: 0         0 (was 1)
     *      SCLK_EN: always     1 (was 0)
     *      SCKE_EN: disable    0
     *      Reserved: 0         0
     *      BURST_EN: enabled   1
     */
    ldr     r2, =0x00000090
    str     r2, [r1, #0x28]

    /* MRSRB6 */
    ldr     r2, =0x00000030
    str     r2, [r1, #0x2C]
    /* MRSRB7 */
    str     r2, [r1, #0x30]
    
    /*
        0x56000000 0x1FFFCFF
                 4 0x1FFFEFF
        0X4800002C 0X0
        0X560000
    */

    /* GPACON */
    mov     r1, #0x56000000 
    ldr     r2, =0x00FFFFFF
    str     r2, [r1]

#if 0
    /* GPACON */
    mov     r1, #0x56000000
    ldr     r2, =0x01FFFCFF /* 0x01FFFCFF */
    str     r2, [r1]
    
    /* GPADAT */
    ldr     r2, =0x01FFFEFF
    str     r2, [r1, #0x04]

    /* MRSRB6 */
    mov     r1, #0x48000000
    mov     r2, #0x00000000
    str     r2, [r1, #0x2C]

    /* GPADAT */
    mov     r1, #0x56000000
    ldr     r2, =0x01FFFFFF     
    str     r2, [r1, #0x04]

    /* MRSRB6 */
    mov     r1, #0x48000000
    mov     r2, #0x00000030
    str     r2, [r1, #0x2C]

    /* GPACON */
    mov     r1, #0x56000000 
    mov     r2, #0x01FFFFFF
    str     r2, [r1]

    /* End of the unknown */
#endif

    /* The builds have two potential load addresses, one being from flash, 
     * and the other from some "unknown" location right now the assumption
     * is that the code is not at 0x3000000.
     */   
    /* get the high part of our execute address (where am I) */
    ldr     r0, =0xfffff000
    and     r0, pc, r0

    /* Copy code to 0x30000000 */
    ldr     r2, = _vectorstart
    ldr     r3, = _initdata_end

    sub     r2, r3, r2           /* length of loader */
    
    ldr     r1, =0x30000000     /* copy location */   
    
    bl      word_copy

    ldr     r1, =donecopy
    ldr     r2, =0x30000000
    add     r1, r1, r2
    mov     pc, r1           /* The code is located where we want it-jump*/

donecopy: 

    /* Setup the MMU, start by disabling */

    mrc     p15, 0, r0, c1, c0, 0  
    bic     r0, r0, #0x41      /* disable mmu and dcache */
    bic     r0, r0, #0x1000   /* disable icache */
    mcr     p15, 0, r0, c1, c0, 0

    bl      ttb_init
    
    ldr     r0, =0x0
    ldr     r1, =0x0
    ldr     r2, =0x1000
    mov     r3, #0
    bl      map_section

    ldr     r0, =0x30000000
    ldr     r1, =0x0
    mov     r2, #32
    mov     r3, #12
    bl      map_section

    ldr     r0, =0x31FD6800     /* FRAME */
    mov     r1, r0
    mov     r2, #1
    mov     r3, #4
    bl      map_section        
    
    bl      enable_mmu   

    /* Initialise bss section to zero */
    ldr     r2, =_edata
    ldr     r3, =_end
    mov     r4, #0
bsszero:
    cmp     r3, r2
    strhi   r4, [r2], #4
    bhi     bsszero
    
    /* Set up some stack and munge it with 0xdeadbeef */
    ldr     sp, =stackend
    mov     r3, sp
    ldr     r2, =stackbegin
    ldr     r4, =0xdeadbeef
stackmunge:
    cmp     r3, r2
    strhi   r4, [r2], #4
    bhi     stackmunge

     /* Set up stack for IRQ mode */
    msr     cpsr_c, #0xd2
    ldr     sp, =irq_stack
    /* Set up stack for FIQ mode */ 
    msr     cpsr_c, #0xd1
    ldr     sp, =fiq_stack

    /* Let abort and undefined modes use IRQ stack */
    msr     cpsr_c, #0xd7
    ldr     sp, =irq_stack
    msr     cpsr_c, #0xdb
    ldr     sp, =irq_stack
    /* Switch to supervisor mode */
    msr     cpsr_c, #0xd3
    ldr     sp, =stackend

    /* Start the main function */
    ldr     pc, =main

    /* Should never get here, but let's restart in case (also needed when
     * linking)
     */
    b       vectors

/* All illegal exceptions call into UIE with exception address as first
   parameter. This is calculated differently depending on which exception
   we're in. Second parameter is exception number, used for a string lookup
   in UIE.
 */
undef_instr_handler:
    mov    r0, lr
    mov    r1, #0
    b      UIE

/* We run supervisor mode most of the time, and should never see a software
   exception being thrown. Perhaps make it illegal and call UIE?
 */
software_int_handler:
reserved_handler:
    movs   pc, lr

prefetch_abort_handler:
    sub    r0, lr, #4
    mov    r1, #1
    b      UIE

data_abort_handler:
    sub    r0, lr, #8 
    mov    r1, #2
    b      UIE

#if defined(BOOTLOADER)
fiq_handler:
    b UIE
#endif

UIE:
    b UIE

/*
 * Function: rom_shutdown
 *  Variables:
 *      none
 */

.section    .init.text, "ax", %progbits
.align      0x04
.global     rom_shutdown
.type       rom_shutdown, %function
rom_shutdown:
    /* Turn off the MMU */
    mrc     p15, 0, r1, c1, c0, 0
    bic     r1, r1, #0x0001
    mcr     p15, 0, r1, c1, c0, 0

    /* Taken from 0x10010 */
    ldr     r2, =0x56000014 //GPBDAT     
    ldr     r1, =0x56000010 //GPBCON     
    ldr     r3, =0x00015450 
    ldr     r8, =0x56000024 //GPCDAT     
    ldr     r10, =0x56000020 //GPCCON    
    ldr     r5, =0xAAA054A8 
    ldr     r6, =0x56000024 //GPDDAT     
    ldr     r7, =0x56000030 //GPDCON     
    ldr     r12, =0x56000044 //GPEDAT    
    ldr     lr, =0x56000040 //GPECON     
    ldr     r0, =0x56000054 //GPFDAT     
    mov     r4, #0          
    str     r4, [r2]        
    str     r3, [r1]        
    ldr     r1, =0xAAA0AAA5 
    ldr     r2, =0xAA8002AA 
    mov     r3, #0x80       
    str     r3, [r8]        
    add     r3, r3, #0x980  
    str     r5, [r10]       
    mov     r5, #4          
    str     r4, [r6]        
    str     r1, [r7]        
    str     r4, [r12]       
    str     r2, [lr]        
    str     r4, [r0],#(0x56000064-0x56000054) //(GPGDAT - GPFDAT) 
    add     r12, r12, #(0x56000060-0x56000044)//(GPGCON - GPEDAT) 
    ldr     r1, =0x56000050 //GPFCON     
    ldr     r2, =0x01401002 
    mov    lr, #0xFFFFFFFF
    str     r3, [r1],#(0x56000074-0x56000050)// (GPHDAT - GPFCON) 
    str     r4, [r0],#(0x56000060-0x56000054)// (GPGCON - GPFDAT) 
//    str     r2, [r12]
    ldr     r2, =0x140A5    
    mov     r3, #0x81       
    str     r3, [r1]        
    ldr     r12, =0x4A000008 //INTMSK    

//    mov     r3, #0x200000   // disable EINT13
    ldr     r3, =0xFFFFFECF

    str     r2, [r0]        // GPFDAT=0x140A5
    ldr     r2, =0x56000088 //EXTINT0    
    ldr     r0, =0x4A000010 //INTPND 

    add     r1, r1, #(0x56000068 - 0x56000050) // (GPGUP - GPFCON) (0x18)
    str     lr, [r12]       /* INTMSK = 0xFFFFFFFF */
    str     r3, [r2],#(0x560000A4 - 0x56000088) // (EINTMASK - EXTINT0) disable EINT13 (0x1C)
//    mov    r3, #0xFFFFFECF
    mov    r3, #0xFFFFFEFF
    str     r5, [r1],#(0x56000074-0x56000058) //(GPHDAT - GPFUP) (0x1C) DCLKCON
    str     r3, [r2]

    ldr     r11, =0x56000060
    ldr     r6, =0x01401002
    str     r6, [r11]

//    add     r3, r3, #0x00000100 
    ldr     r3, =0xFFFFFFDF;
    str     r3, [r12]       // disable INT_TICK

    mov     r3, #0x4A000000     
    add     r2, r2, #(0x560000B0-0x56000088) //(GSTATUS1 - EXTINT0) //; 0x600 (0x28)
    str     lr, [r1]        
    str     lr, [r3]        
    mov     r3, #0x600      
    str     lr, [r0]        
    str     r3, [r2]        // GSTATUS1 = 0x600 /* what for ??? */

    ldr     r12, =0x56000080 //MISCCr    
    mov     r2, #0x58000000 //ADCCON     
    str     r5, [r2]        
    add     r2, r2, #(0x4D000000-0x58000000) //(LCDCON1 - ADCCON) // LCDCON1 (0xF5000000)
    ldr     r3, [r12]       
    ldr     r0, =0x48000024 // REFRESH   
    bic     r3, r3, #0x700000 
    bic     r3, r3, #0x3000
    orr     r3, r3, #0x600000
    orr     r3, r3, #0x3000 
    str     r3, [r12]       // MISCCR = (MISCCR & ~0x100000) | 0x603000;
    /* clear [20] BATTFLT_FUNC - BATT_FLT function On/Off.
     *            0, Battery fault function will be turned on.
     * set [21] BATTFLT_INTR - BATT_FLT Interrupt On/Off.
     *            1, Battery fault interrupt will be masked by hardware.
     * set [13] SEL_SUSPND1 - USB Port 1 Suspend mode
     *            1= suspend mode
     * set [12] SEL_SUSPND0 - USB Port 0 Suspend mode
     *            1= suspend mode
    */
    mov     r3, #0x4C000000 // LOCKTIME   
    str     r4, [r2]     
  
    str     lr, [r3]        
    ldr     r1, [r0]        
    ldr     lr, =0x4C00000C // CLKCON     
//    str     r1, [r11,#-0x28] 
    ldr     r2, [lr]        
//    str     r2, [r11,#-0x28] 
    ldr     r3, [r0]        
    Orr     r3, r3, #0x00400000
    /* REFRESH
     * [22] TREFMD - SDRAM Refresh Mode
     */
    str     r3, [r0]        
    ldr     r2, [r12]       

    ldr     r3, =0x00004018
    /* [3] SLEEP - Control SLEEP mode of S3C2440X.
     * [4] NAND - Control HCLK into NAND Flash Controller block.
     * [14] RTC - Control PCLK into RTC control block.
     */
    orr     r2, r2, #0x000E0000 
    /* [17] nEN_SCLK0 - SCLK0 output enable (1: SCLK 0 = 0)
     * [18] nEN_SCLK1 - SCLK1 output enable (1: SCLK 1 = 0)
     * [19] OFFREFRESH - Self refresh retain enable after wake-up from sleep
     */

    str     r2, [r12]       
    str     r3, [lr]
    
    1:
    b 1b
.ltorg  
.size       rom_shutdown, .-rom_shutdown

.section .text
/* 256 words of IRQ stack */
    .space 256*4
irq_stack:

/* 256 words of FIQ stack */
    .space 256*4
fiq_stack:

