/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 by Linus Nielsen Feltzing
 * Copyright (C) 2010 by Michael Sevakis
 *
 * 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"

/* PortalPlayer 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>
 *
 */
    .equ    PROC_ID,     0x60000000
    .equ    CPU_IDIS,    0x60004028
    .equ    CPU_CTRL,    0x60007000
    .equ    CPU_STATUS,  0x60007000
    .equ    COP_IDIS,    0x60004038
    .equ    COP_CTRL,    0x60007004
    .equ    COP_STATUS,  0x60007004
    .equ    CPU_SLEEPING,0x80000000
    .equ    COP_SLEEPING,0x80000000
    .equ    SLEEP,       0x80000000
    .equ    WAKE,        0x00000000
    .equ    MMAP_LOG,    0xf000f000 /* MMAP0 */
    .equ    MMAP_PHYS,   0xf000f004
    .equ    INT_VECT_TBL,0x6000f100
    .equ    CACHE_CTRL,  0x6000c000
    .equ    CACHE_ENAB,  0x1
    .equ    CACHE_OP_COMMIT_DISCARD, 0x1
    .equ    CACHE_OP_COMMIT        , 0x0
#if MEMORYSIZE > 32
    .equ    MMAP_MASK,   0x00003c00
#else
    .equ    MMAP_MASK,   0x00003e00
#endif
    .equ    MMAP_FLAGS,  0x00000f84

/*
 * Entry point
 */
    .section .init.text,"ax",%progbits
    .global  start
start:
    b       newstart

#ifdef IPOD_ARCH
.align 8    /* starts at 0x100 */
.global boot_table
boot_table:
    /* here comes the boot table, don't move its offset - preceding
       code+data must stay <= 256 bytes */
    .space  400
#else /* !IPOD_ARCH */
    /* (more than enough) space for exception vectors and mi4 magic */
    .space  68*4
#endif /* IPOD_ARCH */

newstart:
    msr     cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */
    adr     r4, start     /* cache initial load address */

    /* Copy startup stub to IRAM since we need to both move the bootloader's
     * location, which could overlap itself, and setup the memory mapper. */
    adr     r0, start_stub_begin
    mov     r1, #0x40000000
    adr     r2, start_stub_end
1:
    ldr     r3, [r0], #4
    str     r3, [r1], #4
    cmp     r0, r2
    blo     1b
    mov     pc, #0x40000000

start_stub_begin:
    ldr     r0, =PROC_ID
    ldrb    r0, [r0]
    cmp     r0, #0x55
    beq     cpu

cop:
    mov     r0, #CACHE_OP_COMMIT_DISCARD
    bl      cache_operation

    ldr     r1, =COP_CTRL
    mov     r0, #SLEEP

    /* sleep us (co-processor) while bootloader is copied */
    str     r0, [r1]
    nop
    nop
    nop

    /* branch to final physical load address */
    ldr     r2, =1f
    and     r4, r4, #0xfc000000
    add     pc, r2, r4
1:
    /* wait for bootloader to finish */
    str     r0, [r1]
    nop
    nop
    nop

    /* branch to the address returned by main() */
    adr     r0, startup_loc
    ldr     pc, [r0]

cpu:
    /* wait for COP to sleep */
    ldr     r1, =COP_STATUS
1:
    ldr     r0, [r1]
    tst     r0, #COP_SLEEPING
    beq     1b

    mov     r0, #CACHE_OP_COMMIT_DISCARD
    bl      cache_operation

    /* move bootloader to the correct load address if needed */
    ldr     r1, =_loadaddress
    cmp     r4, r1
    ldrne   r2, =_loadaddressend
    movne   r0, r4
    sublo   r3, r2, r1 /* size */
    addlo   r0, r0, r3 /* initial load end addr */
1: /* lower to higher move - copy up */
    cmphi   r2, r1
    ldrhi   r3, [r0], #4
    strhi   r3, [r1], #4
    bhi     1b
1: /* higher to lower move - copy down */
    cmplo   r1, r2
    ldrlo   r3, [r0, #-4]!
    strlo   r3, [r2, #-4]!
    blo     1b

    mov     r0, #CACHE_OP_COMMIT
    bl      cache_operation

    and     r4, r4, #0xfc000000

    ldr     r0, =MMAP_FLAGS
    orr     r0, r0, r4 /* adjust for execute address */
    ldr     r1, =MMAP_MASK
    ldr     r2, =MMAP_LOG
    ldr     r3, =MMAP_PHYS
    str     r1, [r2] /* MMAP_LOG = MMAP_MASK */
    str     r0, [r3] /* MMAP_PHYS = MMAP_FLAGS | SDRAM base addr */

    /* wake the COP to jump it to the correct place */
    ldr     r1, =COP_CTRL
    mov     r0, #WAKE
    str     r0, [r1]

    /* wait for COP to halt then loading may proceed */
    ldr     r1, =COP_STATUS
1:
    ldr     r0, [r1]
    tst     r0, #COP_SLEEPING
    beq     1b

    ldr     r0, =start_stub_end
    add     pc, r0, r4

cache_operation: /* (bool commit_discard) */
    ldr     r2, =CACHE_CTRL
    ldr     r1, [r2]
    tst     r1, #CACHE_ENAB
    bxeq    lr
    cmp     r0, #CACHE_OP_COMMIT
    ldr     r0, =0xf000f044
    ldr     r1, [r0]
    orrne   r1, r1, #0x6
    orreq   r1, r1, #0x2
    str     r1, [r0]
1:
    ldr     r1, [r2]
    tst     r1, #0x8000
    bne     1b
    bx      lr
    .ltorg /* constants used in stub come with us to IRAM */
start_stub_end:
    /* now executing from final physical address */

    /* copy the vector addresses to the table */
    ldr     r0, =INT_VECT_TBL
    adr     r1, vectorsstart
    adr     r2, vectorsend
1:
    cmp     r2, r1
    ldrhi   r3, [r1], #4
    strhi   r3, [r0], #4
    bhi     1b

    /* Copy the IRAM */
    ldr     r0, =_iramcopy
    ldr     r1, =_iramstart
    ldr     r2, =_iramend
1:
    cmp     r2, r1
    ldrhi   r3, [r0], #4
    strhi   r3, [r1], #4
    bhi     1b

    mov     r0, #0

    /* Zero out IBSS */
    ldr     r1, =_iedata
    ldr     r2, =_iend
1:
    cmp     r2, r1
    strhi   r0, [r1], #4
    bhi     1b

    /* Initialise bss/ncbss sections to zero */
    ldr     r1, =_edata
    ldr     r2, =_end
1:
    cmp     r2, r1
    strhi   r0, [r1], #4
    bhi     1b

    /* Set up stack for IRQ mode */ 
    msr     cpsr_c, #0xd2 /* IRQ/FIQ disabled */
    ldr     sp, =irq_stack
    /* Let svc, abort and undefined modes use irq stack */
    msr     cpsr_c, #0xd3
    ldr     sp, =irq_stack
    msr     cpsr_c, #0xd7 /* IRQ/FIQ disabled */
    ldr     sp, =irq_stack
    msr     cpsr_c, #0xdb /* IRQ/FIQ disabled */
    ldr     sp, =irq_stack

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

    /* Set up some stack and munge it with 0xdeadbeef */
    ldr     r0, =0xdeadbeef
    ldr     r1, =stackbegin
    ldr     sp, =stackend
1:
    cmp     sp, r1
    strhi   r0, [r1], #4
    bhi     1b

    /* execute the loader - this will load an image to 0x10000000 */
    ldr     r0, =main
    mov     lr, pc
    bx      r0

    /* store actual startup location returned by main() */
    ldr     r1, =startup_loc
    str     r0, [r1]

    /* write back anything loaded + startup_loc */
    mov     r0, #CACHE_OP_COMMIT
    bl      cache_operation

    mov     r0, #0

    /* disable memory mapper */
    ldr     r1, =MMAP_LOG
    ldr     r2, =MMAP_PHYS
    str     r0, [r1]
    str     r0, [r2]

    /* bring COP back to life */
    ldr     r1, =COP_CTRL
    mov     r0, #WAKE
    str     r0, [r1]

    /* after this point, r0-r3 are reserved for OF magic */

#if defined(SANSA_C200) || defined(PHILIPS_HDD1630)
    /* Magic for loading the c200 OF */
    ldr     r0, =0xb00d10ad
    mov     r1, #0x700
    ldr     r2, =0xfff0
    mov     r3, #0x7
#endif

#if defined(PHILIPS_HDD6330)
    /* Magic for loading the HDD6XX0 OF */
    ldr     r0, =0xb00d10ad
    mov     r1, #0x800
    ldr     r2, =0xfff0
    mov     r3, #0x7
#endif

    /* branch to the address returned by main() */
    adr     r4, startup_loc
    ldr     pc, [r4]

startup_loc:
    .word   0x00000000

/* exception handlers: will be copied to local vector table */
vectorsstart:
    .word   newstart 
    .word   undef_instr_handler
    .word   software_int_handler
    .word   prefetch_abort_handler
    .word   data_abort_handler
    .word   reserved_handler
    .word   irq_handler
    .word   fiq_handler
vectorsend:

    .text

/* 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:
    sub     r0, lr, #4
    mov     r1, #0
    b       UIE

/* We run sys mode most of the time, and should never see a software
   exception being thrown. Make it illegal and call UIE.
 */
software_int_handler:
reserved_handler:
    sub     r0, lr, #4
    mov     r1, #4
    b       UIE

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

/* should never happen in the bootloader */
fiq_handler:
    subs    pc, lr, #4

/* 256 words of IRQ stack */
    .section .bss
    .balign 16
    .space  256*4
irq_stack:
