/**
 * \file  : reset.S
 * \date  : 01/12/2012
 * \author: Cesar FUGUET & Manuel BOUYER & Alain Greiner
 *
 * This is a boot code for a generic multi-clusters / multi-processors
 * TSAR architecture (up to 256 clusters / up to 4  processors per cluster). 
 * There is one XICU, one TTY, one DMA and one stack segment per cluster.
 * segment base adresses = base + cluster_segment_increment*cluster_id
 *
 * - Each processor initializes the Status Register (SR) to disable interrupts.
 * - Each processor initializes the Count Register.
 * - Each processor initialises its private XICU Write Triggered Interruption
 *   mask register.
 * - Only processor 0 initializes the stack pointer ($29).
 * - Only processor 0 (boot processor) executes the boot_load_elf function to
 *   load in memory the boot loader stored in the block BOOT_LOADER_LBA of 
 *   the disk.
 * - All non-boot processors wait in a low power consumption mode that the
 *   processor 0 wakes them using the IPI (Inter Processor Interruption)
 *   functionality of the XICU device.
 */

    #include <defs.h>
    #include <mips32_registers.h>

    .section .boot,"ax",@progbits

    .extern seg_stack_base

    .extern dtb_addr
    .extern boot_putc
    .extern boot_getc
    .extern boot_ioc_read
    .extern boot_elf_loader
    .extern boot_memcpy
    .extern boot_puts
    .extern boot_putx
    .extern boot_putd
    .extern boot_ioc_init

    .globl  boot                    /* Make reset an external symbol */
    .ent    boot

    .align  2
    .set noreorder

boot:
    b       _boot                   /* 0xbfc0000 */
    nop                             /* 0xbfc0004 */

    /*  Addresses of the functions provided by this pre-loader */

    .word   BOOT_VERSION            /* 0xbfc0008 */
    .word   dtb_addr                /* 0xbfc000c */
    .word   boot_putc               /* 0xbfc0010 */
    .word   boot_getc               /* 0xbfc0014 */
    .word   boot_ioc_read           /* 0xbfc0018 */
    .word   boot_elf_loader         /* 0xbfc001C */
    .word   boot_memcpy             /* 0xbfc0020 */
    .word   boot_puts               /* 0xbfc0024 */
    .word   boot_putx               /* 0xbfc0028 */
    .word   boot_putd               /* 0xbfc002C */

_boot:
    /* Disable interruptions, keep STATUSbev enabled */

    li      k0,     (1 << 22)
    mtc0    k0,     CP0_STATUS

    /* Computes proc_id, local_id, cluster_id, and cluster_increment */

    mfc0    k0,     CP0_EBASE
    andi    t0,     k0,     0x3FF   /* t0 <= proc_id (at most 1024 processors)    */

    move    t3,     t0

    la      k0,     NB_PROCS        /* k0 <= number of processors per cluster     */
    divu    t3,     k0
    mfhi    t1                      /* t1 <= local_id   = proc_id % NB_PROCS      */
    mflo    t2                      /* t2 <= cluster_id = proc_id / NB_PROCS      */

    la      k0,     NB_CLUSTERS
    li      t3,     0x80000000
    divu    t3,     k0
    mflo    t4
    sll     t4,     1               /* t4 <= cluster_increment = 4G / NB_CLUSTERS */

    mult    t4,     t2 
    mflo    t5                      /* t5 <= cluster_id * cluster_increment       */
 
    /* Initialization of the count register in the coprocessor 0 */

    mtc0    zero,   CP0_COUNT

    /* In each cluster, the ICU base address depends on the cluster_id */

    la      t3,     ICU_BASE
    addu    t3,     t3,     t5      /* t3 <= ICU_BASE +                       */
                                    /*       (cluster_id * cluster_increment) */

    /**
     * Compute the output index for the Write Triggered Interruption mask. 
     * Each processor enable the WTI for its irq output 
     * Each processor may have IRQ_PER_PROC private irq outputs from
     * the XICU
     */

    move    t4,     t1              /* t4 <= local_id                   */
    li      t5,     IRQ_PER_PROC    /* t5 <= IRQ_PER_PROC               */
    multu   t4,     t5              
    mflo    t6                      /* t6 <= IRQ_PER_PROC * local_id    */
    sll     t4,     t6,     2       /* t4 <= OUT_INDEX = t6 * 4         */

    li      t5,     (0xC << 7)      /* t5 <= FUNC      = XICU_MSK_WTI   */
    or      t4,     t4,     t5      /* t4 <= FUNC | INDEX | 00          */
    or      t5,     t3,     t4      /* t5 <= &XICU[MSK_WTI][OUT_INDEX]  */
    
    /* Compute and set WTI mask */

    li      t4,     1
    sllv    t4,     t4,     t1      /* Set XICU[MSK_WTI][INDEX][local_id] */
    sw      t4,     0(t5)           /* XICU[MSK_WTI][INDEX] <= t4         */

    /**
     * We have:
     * t0: global id
     * t1: local id
     * t2: cluster id
     * t3: xicu base address
     * 
     * Only processor 0 in cluster 0 executes the boot loader 
     */

    bne     zero,   t0,     _reset_wait
    nop

    /* Initializes stack pointer */

    la      k1,     seg_stack_base
    li      k0,     BOOT_STACK_SIZE
    addu    sp,     k1,     k0      /* sp <= seg_stack_base + BOOT_STACK_SIZE */

#ifndef SOCLIB_IOC

    /* Initialize the block device */

    la      k0,     boot_ioc_init
    jalr    k0
    nop

#endif

    /**
     * Jump to the boot elf loader routine 
     * Passing as argument the block number in which it must be
     * the executable elf file to load
     */

    la      k0,     boot_elf_loader
    li      a0,     BOOT_LOADER_LBA
    jalr    k0
    nop

    /** 
     * We jump to the entry point address defined in the 
     * ELF file. This address is returned by boot_elf_loader function.
     * All function arguments are 0
     */

    move    a0,     zero
    move    a1,     zero
    move    a2,     zero
    move    a3,     zero
    jr      v0
    nop

/**
 * Wait in low power consumption mode until the application wakes us.
 * The application wakes up the non-boot CPUs using a IPI with a non-0
 * value in the mailbox. This non-0 value is the address to jump to.
 */

_reset_wait:
    /**
     * We have:
     * t0: global id
     * t1: local id
     * t2: cluster id
     * t3: xicu base address
     */

    sll     t4,     t1,     2       /* t4 <= local_id * 4             */
    addu    t5,     t4,     t3      /* t5 <= &XICU[WTI_REG][local_id] */

    wait

    lw      k0,     0(t5)           /* k0 <= XICU[WTI_REG][local_id]  */
    jr      k0
    nop

/* Exception entry point */
.org 0x0380
_excep:
    mfc0    a0, CP0_STATUS          /* first arg is status */
    mfc0    a1, CP0_CAUSE           /* second arg is cause */
    mfc0    a2, CP0_EPC             /* third argc is epc   */
    nop
    j       handle_except
    nop

    .end boot

    .set reorder

/*
 * vim: tabstop=4 : shiftwidth=4 : expandtab
 */
