/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * 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"

#define CACHE_NONE  0
#define CACHE_ALL   0x0C
#define BUFFERED    0x04
/****************************************************************************/
#ifdef TOSHIBA_GIGABEAT_F

/* Clock and Power Management setup values */
#define VAL_CLKDIV  0x7
#define VAL_UPLLCON 0x0003C042
#define VAL_MPLLCON 0x000C9042

/* Memory Controller setup */
    /* 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
     */
#define VAL_BWSCON    0x01055102    
    /* 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
     */
#define VAL_BANKCON0  0x00000D60
    /* 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
     */
#define VAL_BANKCON1  0x00000000
    /* 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
     */
#define VAL_BANKCON2  0x00001FA0
#define VAL_BANKCON3  0x00001D80
#define VAL_BANKCON4  0x00001D80
#define VAL_BANKCON5  0x00000000
    /* 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
     */
#define VAL_BANKCON6  0x00018005
#define VAL_BANKCON7  0x00018005

#define VAL_REFRESH   0x00980501
    /* 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
     */
#define VAL_BANKSIZE  0x00000090
#define VAL_MRSRB6    0x00000030
#define VAL_MRSRB7    0x00000030
#define VAL_GPACON    0x00FFFFFF

/****************************************************************************/
#elif defined (MINI2440)

/* For Mini2440 board or compatible */
/* Clock and Power Management setup values */
/* NB: clock settings must match values in s3c2440/system-target.h */
#define VAL_CLKDIV    0x5         /* HCLK = FCLK/4, PCLK = HCLK/2 */
#define VAL_UPLLCON   0x00038022  /* UCLK = 48 MHz */
#define VAL_MPLLCON   0x000C3041  /* FCLK = 406 MHz */


/* Memory Controller setup */
#define VAL_BWSCON    0x22111112    
#define VAL_BANKCON0  0x00002F50  
#define VAL_BANKCON1  0x00000700
#define VAL_BANKCON2  0x00000700
#define VAL_BANKCON3  0x00000700
#define VAL_BANKCON4  0x00000700
#define VAL_BANKCON5  0x0007FFFC
#define VAL_BANKCON6  0x00018009
#define VAL_BANKCON7  0x00018009

#define VAL_REFRESH   0x008E04EB
#define VAL_BANKSIZE  0x000000B2
#define VAL_MRSRB6    0x00000030
#define VAL_MRSRB7    0x00000030

#define VAL_GPACON    0x00FFFFFF
#define VAL_GPFCON    0x000055AA
#define VAL_GPGCON    0xAA2A0128
#define VAL_GPGDAT    0x0000

#else
#error Unknown target
#endif
/****************************************************************************/

/* 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 (0x040A0020)
 */
    b   rom_shutdown

/* Add some strings to detect the bootloader in flash and give it a version
 * number.  (0x040A0028, 0x040A002C)
 */
.string "ROCKBOX\0"
.string "R 03.00\0"

/*
 * 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 
.global     start
start:

    /* Get the execute address; R0 is used to store the address and it should
     * not be written to till the rest of the execution checks are done below.
     * This is done first thing since we have to check if the code was started
     * with the old rockbox bootloader that offset the image by 100 bytes.
     */
    ldr     r0, =0xffffff00
    and     r0, pc, r0

    /************************** DO NOT WRITE TO R0 ***************************/

#ifdef TOSHIBA_GIGABEAT_F
    /* Check if the code is running from flash. If not skip all these checks */
    cmp     r0, #0xA0000
    bne     poweron

    /* Point LCDSADDR1 to the boot logo for lcd_init_device in lcd-meg-fx.c */
    mov     r2, #0x4D000000
    ldr     r1, =0x020200A8
    str     r1, [r2, #0x14]
    
    /* Did an RTC event wake the player up? */ 
    mov     r2, #0x4A000000
    ldr     r1, [r2]
    ands    r1, r1, #0x40000000

    /* Store a flag in GSTATUS3 to indicate that the bootloader is flashed */
    ldr     r2, =0x560000b8
    mov     r1, #0x02
    
    /* Woke up with the alarm? - store a flag in GSTATUS3 */
    orrne   r1, r1, #0x01
    str     r1, [r2]
    bne     poweron

    /* Set GPG up to read power and menu status */
    ldr     r2, =0x56000050
    ldr     r1, [r2, #0x18]
    orr     r1, r1, #0x03
    str     r1, [r2, #0x18]

    /* Check if menu is held down */
    ldr     r1, [r2, #0x14]
    ands    r3, r1, #0x02
    bne     bootOF

    /* Check if power is held down  */
    ands    r3, r1, #0x01
    bne     poweron

    /* Set GPF up to read charger connection if power is not held down */
    ldr     r1, [r2, #0x08]
    orr     r1, r1, #0x10
    str     r1, [r2, #0x08]

    /* Check if charger is connected */
    ldr     r1, [r2, #0x04]
    ands    r1, r1, #0x10
    beq     poweron

bootOF:
    /* power is not down || menu is held || the charger is not connected */
    mov     pc, #0x70
#endif

poweron:
    /* enter supervisor mode, disable IRQ */
    msr     cpsr, #0xd3 

    /* 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]

#ifdef TOSHIBA_GIGABEAT_F
    /* 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.
     */

    /* 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 0xA0000?  If so skip copy. */
    cmplt   r0, #0xA0000
    beq     skipreset

    /****************************** OK TO USE R0 *****************************/
    
    /* 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
#endif

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, #VAL_CLKDIV
    mov     r1, #0x4C000000
    str     r2, [r1, #0x14]

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

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

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

    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop

    ldr     r2, =VAL_MPLLCON
    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 */

    ldr     r2, =VAL_BWSCON
    mov     r1, #0x48000000
    str     r2, [r1]

    /* BANKCON0 */
    ldr     r2, =VAL_BANKCON0
    str     r2, [r1, #0x04]

    /* BANKCON1 */
    ldr     r2, =VAL_BANKCON1
    str     r2, [r1, #0x08]

    /* BANKCON2 */
    ldr     r2, =VAL_BANKCON2
    str     r2, [r1, #0xC]

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

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

    /* BANKCON6/7 */
    ldr     r2, =VAL_BANKCON6
    str     r2, [r1, #0x1C]
    /* BANKCON7 */
    str     r2, [r1, #0x20]

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

    /* BANKSIZE */
    ldr     r2, =VAL_BANKSIZE
    str     r2, [r1, #0x28]

    /* MRSRB6 */
    ldr     r2, =VAL_MRSRB6
    str     r2, [r1, #0x2C]
    /* MRSRB7 */
    str     r2, [r1, #0x30]
    
    /* RMC: I guess this is some notes about Gigabeat */ 
    /*
        0x56000000 0x1FFFCFF
                 4 0x1FFFEFF
        0X4800002C 0X0
        0X560000
    */

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

#if 0
    /* GPGCON */
    ldr     r2, =VAL_GPGCON
    str     r2, [r1, #0x60]
    ldr     r2, =VAL_GPGDAT
    str     r2, [r1, #0x64]
#endif

    /* Copy from current location (from NOR Flash if bootloader, load buffer if
        firmware) to RAM */
    
    /* Gigabeat: 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 from address */

    /* SDRAM starts at 0x30000000 (physical address) */
    ldr     r1, =0x30000000      /* copy To address */   
    ldr     r2, = _vectorstart
    ldr     r3, = _initdata_end
    sub     r2, r3, r2           /* length of loader */
    bl      word_copy

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

donecopy: 

    /* Setup the MMU, start by disabling */

    mrc     p15, 0, r0, c1, c0, 0  
    bic     r0, r0, #0x5           /* 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, #CACHE_NONE
    bl      map_section

    ldr     r0, =0x30000000
    ldr     r1, =0x0
    mov     r2, #MEMORYSIZE
    mov     r3, #CACHE_ALL
    bl      map_section

    ldr     r0, =LCD_FRAME_ADDR    /* LCD Frame buffer */
    mov     r1, r0
    mov     r2, #1
    mov     r3, #BUFFERED
    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 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 svc, abort and undefined modes use irq stack */
    msr     cpsr_c, #0xd3
    ldr     sp, =irq_stack
    msr     cpsr_c, #0xd7
    ldr     sp, =irq_stack
    msr     cpsr_c, #0xdb
    ldr     sp, =irq_stack

    /* Switch to sys mode */
    msr     cpsr_c, #0xdf

    /* 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

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

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

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

UIE:
    b UIE

/* TODO: Review this function - is it target dependent? */
/*
 * 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, #0x00C00000
    /* 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:
