/*******************************************************************************
* Company: Microsemi Corporation
*
* File: main.c
* File history:
*      Revision: 1.0 Date: May 4, 2010
*
* Description:
*
* This sample program reads the executable binary image from UART, programming
* either SPI Flash or External NOR Flash memory and boot. If host loader is running
* with EMC parameter, then the NOR flash will be programmed and boot from NOR flash.
* If host loader is running with SPI parameters, the SPI flash will be programmed
* and copy the SPI content into eSRAM and boot.
*
* Author: Upender Cherukupally
*         Upender.Cherukupally@microsemi.com
*         Corporate Applications Engineering
*
*******************************************************************************/

#include "mss_uart.h"
#include "mss_watchdog.h"
#include "nor.h"
#include "spi_flash.h"


#define BOOT_EMC_FLASH_IMAGE 1

#define BOOT_SPI_FLASH_IMAGE 1

static void spi_flash_loader(void);
static void emc_flash_loader(void);

#ifdef BOOT_EMC_FLASH_IMAGE
static void emc_flash_boot(void);
#endif

#ifdef BOOT_SPI_FLASH_IMAGE
void spi_flash_to_esram(uint32_t srcAddr, uint32_t size);
#endif


#define EXT_FLASH_BASE_ADDR  0x74000000
#define EXT_ASRAM_BASE_ADDR  0x70000000
#define ESRAM_BASE_ADDR      0x20000000

size_t
UART_Polled_Rx
(
    mss_uart_instance_t * this_uart,
    uint8_t * rx_buff,
    size_t buff_size
)
{
    size_t rx_size = 0U;

    while( rx_size < buff_size )
    {
       while ( this_uart->hw_reg_bit->LSR_DR != 0U  )
       {
           rx_buff[rx_size] = this_uart->hw_reg->RBR;
           ++rx_size;
       }
    }

    return rx_size;
}


/*==============================================================================
 * main function.
 */


int main()
{

    uint8_t rx_buff[1];
    /*--------------------------------------------------------------------------
     * Disable watchdog.
     */

    MSS_WD_disable();

    /* Initialize and configure UART0. */
    MSS_UART_init( &g_mss_uart0,
                  MSS_UART_57600_BAUD,
                  MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT );

   /* Handshake */

    while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
         ;
    if(rx_buff[0] == 'h')
        MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t *)"a", 1 );

    while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
         ;

    if(rx_buff[0] == 'n')
        MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t *)"d", 1 );

    while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
         ;

    if(rx_buff[0] == 's')
        MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t *)"h", 1 );

    while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
         ;

    if(rx_buff[0] == 'a')
        MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t *)"k", 1 );

    while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
         ;

    if(rx_buff[0] == 'e')
    {


/* EMC or SPI Flash*/
        while( ! UART_Polled_Rx(&g_mss_uart0, rx_buff, 1))
             ;
        if(rx_buff[0] == 's')
        {
            spi_flash_loader();
        }
        else if(rx_buff[0] == 'e')
        {
           emc_flash_loader();
#ifdef BOOT_EMC_FLASH_IMAGE
           emc_flash_boot();
#endif
        }

    }
    return 0;
}



static void emc_flash_loader(void)
{
    size_t rx_size;
    uint8_t img_buffer[1024];
    uint32_t  address;
    int32_t img_size,ii,length;


    /* Read the file and burn into external flash */
    while( ! UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&img_size, 4))
           ;

    emc_init();

    emc_flash_chip_erase(EXT_FLASH_BASE_ADDR);

    MSS_UART_polled_tx( &g_mss_uart0,(const uint8_t *)&img_size, 4 );

    /* Read the file and burn into external flash */
    while( ! UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&address, 4))
        ;

    MSS_UART_polled_tx( &g_mss_uart0,(const uint8_t *)&address, 4 );

    ii = 0;
    while(img_size > 0)
    {
        rx_size =0;
        if (img_size >= 1024)
        {
            length = 1024;
        }
        else
        {
            length = img_size;
        }

        do
        {
            rx_size = UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&img_buffer, length);
        }
        while(!rx_size);

        if(rx_size != length)
        {
            return;
        }
        img_size -= length;
        /* alligning to 32 bit */
        if (length%4)
            length += (4-(length%4));

        emc_flash_write(address + ii, img_buffer, length);

        ii += length;

        MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t *)"a",1);

    }
    MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t *)"y",1);
    return;
}

#ifdef BOOT_EMC_FLASH_IMAGE
static void   emc_flash_boot(void)
{
    unsigned int ii=0;
    unsigned long *srcAddr = (unsigned long *)EXT_FLASH_BASE_ADDR;
    unsigned long *destAddr = (unsigned long *)ESRAM_BASE_ADDR;

    *(volatile unsigned long *)0xE000ED08 = ESRAM_BASE_ADDR;

    for (ii=0; ii<166; ii++ )
    {
        *destAddr++ = *srcAddr++;
    }


    __asm volatile(
      /*==============================================================================
       * bx_user_code
       *------------------------------------------------------------------------------
       * Purpose:
       *  Branch to the user boot code at the completion of the chip boot code.
       *  This function must set the Cortex-M3 Main Stack Pointer and Program Counter
       *  registers to the value expected by the user code as if the user code was the
       *  first piece of code executed by Cortex-M3.
       *
       *------------------------------------------------------------------------------
       * Input parameters:
       *  None.
       *------------------------------------------------------------------------------
       * Theory of operation:
       *  Step 1:
       *      Load the address of the Vector Table Offset Register in R2
       *  Step 2:
       *      Load the address of the vector table into R2.
       *  Step 3:
       *      Dereference the address stored into R2 to load the actual value of the
       *      stack pointer into R0.
       *  Step 4:
       *      Dereference the address stored in R2 + 0x04 to load the user's reset
       *      address into the Link Register. The location at address R2 + 0x04 is the
       *      reset vector, R2 being the base address of the vector table.
       *  Step 5:
       *      Load the content of R0 into the Main Stak Pointer register.
       *  Step 6:
       *      Return from the function by branching to the Link Register. This causes
       *      to branch to the user's boot code.
       *
       *------------------------------------------------------------------------------
       * Note:
       *  This function assumes that the content of the Vector Table Offset Register
       *  (VTOR) is correct when this function is called.
       *  This function uses the VTOR to find the reset address and initial main stack
       *  pointer value instead of assuming that the vector table is located at
       *  address 0x00000000 to cater for the case where the system boot is executed
       *  as part of a debug sessions where the debugger script modified the VTOR
       *  value to reflect the fact that the debugged code executes directly from
       *  eSRAM (address 0x20000000) without remapping the eSRAM to address 0x00000000.
       */
           "ldr r2,=0x20000000    \n"  /* Step 2. */
           "ldr r0,[r2,#0x0]      \n"  /* Step 3. */
           "ldr lr,[r2,#0x4]      \n"  /* Step 4. */
           "msr msp,r0            \n"  /* Step 5. */
           "bx lr                 \n"  /* Step 6. */
   );

}
#endif

static void spi_flash_loader(void)
{
    size_t rx_size;
    uint8_t img_buffer[1024];
    uint32_t  address, oriAddress;
    int32_t img_size,ii,length, oriSize;

    /* Read the file and burn into external flash */
    while( ! UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&img_size, 4))
         ;

    spi_flash_init();
    spi_flash_control_hw(SPI_FLASH_GLOBAL_UNPROTECT,0,NULL);
    spi_flash_control_hw(SPI_FLASH_CHIP_ERASE,0,NULL);

    MSS_UART_polled_tx( &g_mss_uart0,(const uint8_t *)&img_size, 4 );

    oriSize = img_size;

    /* Read the file and burn into external flash */
    while( ! UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&address, 4))
        ;

    MSS_UART_polled_tx( &g_mss_uart0,(const uint8_t *)&address, 4 );
    oriAddress = address;
    ii = 0;
    while(img_size > 0)
    {
        rx_size =0;
        if (img_size >=1024)
        {
            length = 1024;
        }
        else
        {
            length = img_size;
        }

        do
        {
            rx_size = UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&img_buffer, length);
        }
        while(!rx_size);

        if(rx_size != length)
        {
            return;
        }
        img_size -= length;
          /* alligning to 32 bit */
        if ((length%4) != 0)
        {
            length += (4-(length%4));
        }
        spi_flash_write(address + ii, img_buffer, length);

        ii += length;

        MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t *)"a",1);

    }
           /* Verify the last bytes */
    {
        uint8_t img_buffer2[1030];
        ii -= length;
        spi_flash_read(address + ii, img_buffer2, length);
        for(ii=0; ii<length; ii++)
        {
            if(img_buffer[ii] != img_buffer2[ii])
            {
                MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t *)"z",1);
                return;
            }
        }
        MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t *)"y",1);

#ifdef BOOT_SPI_FLASH_IMAGE
        spi_flash_to_esram(oriAddress, oriSize);
#endif

    }
    return;
}


#ifdef BOOT_SPI_FLASH_IMAGE
void spi_flash_to_esram(uint32_t srcAddr, uint32_t size)
{

    uint8_t img_buffer[1024];
    unsigned int ii=0, jj =0;
    unsigned long length = 0;
    unsigned long *exeDestAddr, *exeSrcAddr;

    unsigned char *destAddr = (unsigned char *)EXT_ASRAM_BASE_ADDR;

    *(volatile unsigned long *)0xE000ED08 = ESRAM_BASE_ADDR;

    emc_init();

    ii = 0;
    jj = 0;
    while(size > 0)
    {
        if (size >=1024)
        {
            length = 1024;
        }
        else
        {
            length = size;
        }
        spi_flash_read(srcAddr + jj, img_buffer, length);

        for(ii=0; ii<length; ii++)
        {
            *destAddr++ = img_buffer[ii];
        }
        jj += length;
        size -= length;

    }

    exeDestAddr = (unsigned long *)ESRAM_BASE_ADDR;

    exeSrcAddr = (unsigned long *)EXT_ASRAM_BASE_ADDR;
    for (ii=0; ii<166; ii++ )
    {
       *exeDestAddr++ = *exeSrcAddr++;
    }

    __asm volatile(
      /*==============================================================================
       * bx_user_code
       *------------------------------------------------------------------------------
       * Purpose:
       *  Branch to the user boot code at the completion of the chip boot code.
       *  This function must set the Cortex-M3 Main Stack Pointer and Program Counter
       *  registers to the value expected by the user code as if the user code was the
       *  first piece of code executed by Cortex-M3.
       *
       *------------------------------------------------------------------------------
       * Input parameters:
       *  None.
       *------------------------------------------------------------------------------
       * Theory of operation:
       *  Step 1:
       *      Load the address of the Vector Table Offset Register in R2
       *  Step 2:
       *      Load the address of the vector table into R2.
       *  Step 3:
       *      Dereference the address stored into R2 to load the actual value of the
       *      stack pointer into R0.
       *  Step 4:
       *      Dereference the address stored in R2 + 0x04 to load the user's reset
       *      address into the Link Register. The location at address R2 + 0x04 is the
       *      reset vector, R2 being the base address of the vector table.
       *  Step 5:
       *      Load the content of R0 into the Main Stak Pointer register.
       *  Step 6:
       *      Return from the function by branching to the Link Register. This causes
       *      to branch to the user's boot code.
       *
       *------------------------------------------------------------------------------
       * Note:
       *  This function assumes that the content of the Vector Table Offset Register
       *  (VTOR) is correct when this function is called.
       *  This function uses the VTOR to find the reset address and initial main stack
       *  pointer value instead of assuming that the vector table is located at
       *  address 0x00000000 to cater for the case where the system boot is executed
       *  as part of a debug sessions where the debugger script modified the VTOR
       *  value to reflect the fact that the debugged code executes directly from
       *  eSRAM (address 0x20000000) without remapping the eSRAM to address 0x00000000.
       */
           "ldr r2,=0x20000000    \n"  /* Step 2. */
           "ldr r0,[r2,#0x0]      \n"  /* Step 3. */
           "ldr lr,[r2,#0x4]      \n"  /* Step 4. */
           "msr msp,r0            \n"  /* Step 5. */
           "bx lr                 \n"  /* Step 6. */

    );

}
#endif
