/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2011, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

/**
 * \page eefc_pgm_dual_bank EEFC dual bank programming example
 *
 * \section Purpose
 *
 * This basic example shows how to use the Enhance Embedded Flach (EEFC) peripheral
 * available on the newest Atmel SAM3S microcontrollers. It details steps required to
 * program the internal flash, and manage secure and lock bits.
 *
 * It also demostrate how to use dual plane feature on SAM3SD8 to update
 * firmware/application.
 *
 * \section Requirements
 *
 * This package can be used with SAM3S-EK2.
 *
 * \section Description
 * At startup, this example will show a menu to let user choose:
 *    \code
 *     Menu:
 *       b - Basic flash operation demostration.
 *       d - Firmware update demostration.
 *    \endcode
 *
 * Choose <b>'b'</b> option for basic flash operation demostration,
 * please refer \ref eefc_pgm for detial description.
 *
 * Choose <b>'d'</b> option for firmware update demostration (only available for SAM3SD8).
 * Thanks to dual plane feature in SAM3SD8, it is easy to do application update
 * even the program is running out the flash.
 *
 * The steps of firmware update demostration are:
 *   -# The old firmware is running out of first flash plane
 *   -# Using Xmodem protocol to download the new firmware into second flash plane by UART
 *   -# Swap flash plane 0 and plane 1 by set or clear GPVNM bit 2
 *   -# Reset chip to boot with new firmware
 *
 * \note
 *   - For SAM3SD8, the flash is always seen as a single block, containing
 *     2 planes (plane 0 and plane 1) that can be swapped by GPNVM bit 2.
 *   - And GPNVM bit 1, just tell to remap the Flash block or the ROM at address 0.
 *   - To demostrate firmware update, the application should run out of flash
 *     without toolchain IDE.
 *
 * \section Usage
 *
 * -# Build the program and download it inside the evaluation board. Please
 *    refer to the
 *    <a href="http://www.atmel.com/dyn/resources/prod_documents/6421B.pdf">
 *    SAM-BA User Guide</a>, the
 *    <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6310.pdf">
 *    GNU-Based Software Development</a> application note or to the
 *    <a href="http://www.iar.com/website1/1.0.1.0/78/1/">
 *    IAR EWARM User and reference guides</a>, depending on your chosen solution.
 * -# On the computer, open and configure a terminal application
 *    (e.g. HyperTerminal on Microsoft Windows) with these settings:
 *   - 115200 bauds
 *   - 8 bits of data
 *   - No parity
 *   - 1 stop bit
 *   - No flow control
 * -# Start the application.
 * -# In the terminal window, the following text should appear:
 *    \code
 *     -- EEFC Dual Bank Programming Example xxx --
 *     -- xxxxxx-xx
 *     -- Compiled: xxx xx xxxx xx:xx:xx --
 *
 *     Menu:
 *       b - Basic flash operation demostration.
 *       d - Firmware update demostration.
 *    \endcode
 * -# if you choose 'b' option:
 *    \code
 *     Basic flash operation demostration...
 *     -I- Unlocking last page
 *     -I- Writing last page with walking bit pattern
 *     -I- Checking page contents ......................................... ok
 *     -I- Locking last page
 *     -I- Try to program the locked page...
 *     -I- Please open Segger's JMem program
 *     -I- Read memory at address 0x0043FF00 to check contents
 *     -I- Press any key to continue...
 *     -I- Good job!
 *     -I- Now set the security bit
 *     -I- Press any key to continue to see what happened...
 *     -I- Setting GPNVM #0
 *     -I- All tests done
 *    \endcode
 * -# if you choose 'd' option:
 *    \code
 *    Download new program demostration...
 *    Flash plane 0 and plane 1 are (not) swapped.
 *    Please use xmodem protocol to send a new program.
 *    \endcode
 *    After download, a message will display:
 *    \code
 *    Download done. File size is xxx
 *    Press 's' to switch to new program.
 *    \endcode
 *    If press 's', it will reset the chip and boot from the new firmware.
 *
 * \section References
 * - eefc_pgm_dual_bank/main.c
 * - efc.c
 * - efc.h
 * - flashd.c
 * - flashd.h
 */
/**
 * \file
 *
 * This file contains all the specific code for the eefc_pgm_dual_bank example.
 *
 */
/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "board.h"

#include <stdio.h>
#include <stdarg.h>
#include <assert.h>

void SwapFlashPlaneAndResetChip(uint32_t gpnvmBit2);

/*----------------------------------------------------------------------------
 *        Local variables
 *----------------------------------------------------------------------------*/
/* Receive file buffer */
int8_t gFileBuff[IFLASH_PAGE_SIZE * 16];

/*----------------------------------------------------------------------------
 *        Local functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Display menu
 */
static void DisplayMenu(void)
{
    printf( "\n\rMenu:\n\r" ) ;
    printf( "  b - Basic flash operation demostration.\n\r" ) ;
    printf( "  d - Firmware update demostration.\n\r" ) ;
    printf( "\n\r" ) ;
}

/**
 * \brief Demotrate basic flash operation.
 */
static void DemoFlashBasicOperation(void)
{
    uint32_t i;
    uint32_t error;
    uint32_t pBuffer[IFLASH_PAGE_SIZE / 4];
    uint32_t lastPageAddress;
    volatile uint32_t *pLastPageData;

    printf("\n\rBasic flash operation demostration...\n\r");

    /* Performs tests on last page (to avoid overriding existing program).*/
    lastPageAddress = IFLASH_ADDR + IFLASH_SIZE - IFLASH_PAGE_SIZE;
    pLastPageData = (volatile uint32_t *) lastPageAddress;

    /* Unlock page */
    printf("-I- Unlocking last page\n\r");
    error = FLASHD_Unlock(lastPageAddress, lastPageAddress + IFLASH_PAGE_SIZE, 0, 0);
    assert( !error ) ;

    /* Write page with walking bit pattern (0x00000001, 0x00000002, ...) */
    printf("-I- Writing last page with walking bit pattern\n\r");
    for ( i=0 ; i < (IFLASH_PAGE_SIZE / 4); i++ )
    {
        pBuffer[i] = 1 << (i % 32);
    }
    error = FLASHD_Write(lastPageAddress, pBuffer, IFLASH_PAGE_SIZE);
    assert(!error ) ;

    /* Check page contents */
    printf( "-I- Checking page contents " ) ;
    for (i=0; i < (IFLASH_PAGE_SIZE / 4); i++)
    {
        printf(".") ;
        assert( pLastPageData[i] == (uint32_t)(1 << (i % 32)) ) ;
    }
    printf(" ok \n\r") ;

    /* Lock page */
    printf( "-I- Locking last page\n\r" ) ;
    error = FLASHD_Lock( lastPageAddress, lastPageAddress + IFLASH_PAGE_SIZE, 0, 0 ) ;
    assert( !error ) ;

    /* Check that associated region is locked*/
    printf( "-I- Try to program the locked page... \n\r" ) ;
    error = FLASHD_Write( lastPageAddress, pBuffer, IFLASH_PAGE_SIZE ) ;
    if ( error )
    {
        printf( "-I- The page to be programmed belongs to a locked region.\n\r" ) ;
    }

    printf( "-I- Please open Segger's JMem program \n\r" ) ;
    printf( "-I- Read memory at address 0x%08x to check contents\n\r",(unsigned int)lastPageAddress ) ;
    printf( "-I- Press any key to continue...\n\r" ) ;
    while ( !UART_GetChar() ) ;

    printf( "-I- Good job!\n\r" ) ;
    printf( "-I- Now set the security bit \n\r" ) ;
    printf( "-I- Press any key to continue to see what happened...\n\r" ) ;
    while ( !UART_GetChar() ) ;

    /* Set GPNVM bit 0 (security bit) */
    printf( "-I- Setting GPNVM #%d\n\r", 0 ) ;
    error = FLASHD_SetGPNVM( 0 ) ;
    assert( !error ) ;

    printf( "-I- All tests done\n\r" ) ;
}

/**
 * \brief Media callback for flash download.
 *
 * \param dwMediaAddress Media start address of download
 * \param pBuffer        Data buffer
 * \param dwBufSize      Size of data buffer
 */
static void MediaCallback(uint32_t dwMediaAddress, int8_t *pBuffer, uint32_t dwBufSize)
{
    FLASHD_Write(dwMediaAddress, (void *)pBuffer, dwBufSize);
}

/**
 * \brief Demostrate firmware update
 */
static void DemoFirmwareUpdate(void)
{
    uint32_t fileLength;
    uint32_t gpnvmBit2;
    uint32_t error;
    uint8_t  c;

    printf("\n\rDemo firmware update...\n\r");

    gpnvmBit2 = FLASHD_IsGPNVMSet(2);
    if (gpnvmBit2)
    {
        printf("Flash plane 0 and plane 1 are swapped.\n\r");
    }
    else
    {
        printf("Flash plane 0 and plane 1 are not swapped.\n\r");
    }
    error = FLASHD_Unlock(IFLASH1_ADDR, IFLASH1_ADDR + IFLASH1_SIZE, 0, 0);
    assert( !error ) ;
    printf("Please use xmodem protocol to send a new program.\n\r");
    fileLength = XMODEM_DownloadProgram(gFileBuff, sizeof(gFileBuff), IFLASH1_ADDR, MediaCallback);
    printf("\n\rDownload done. File size is %d\n\r", (int)fileLength);

    printf("Press 's' to switch to new program.\n\r");
    c = UART_GetChar();
    if (c == 's')
    {
        printf("Swap flash plane and reset chip.\n\r");
        SwapFlashPlaneAndResetChip(gpnvmBit2);
    }
}

/*----------------------------------------------------------------------------
 *         Global functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Swap flash plane 0 and plane1, then reset chip.
 * \note This function should run out of sram.
 *
 * \param gpnvmBit2 current gpnvm bit 2 value (0/1)
 */
RAMFUNC void SwapFlashPlaneAndResetChip(uint32_t gpnvmBit2)
{
    Efc      *pEfc = EFC;
    uint32_t  dwStatus;
    Rstc     *pRstc = RSTC;

    /* Disable all interrupt */
    __disable_irq();

    /* Swap flash plane 0 and plane 1 */
    if (gpnvmBit2)
    {
        pEfc->EEFC_FCR = EEFC_FCR_FKEY(0x5A) | EEFC_FCR_FARG(2) | EEFC_FCR_FCMD(EFC_FCMD_CFB) ;
    }
    else
    {
        pEfc->EEFC_FCR = EEFC_FCR_FKEY(0x5A) | EEFC_FCR_FARG(2) | EEFC_FCR_FCMD(EFC_FCMD_SFB) ;
    }
    do
    {
        dwStatus = pEfc->EEFC_FSR ;
    }
    while ( (dwStatus & EEFC_FSR_FRDY) != EEFC_FSR_FRDY ) ;

    /* Reset chip */
    pRstc->RSTC_CR = RSTC_CR_KEY(0xA5u) | RSTC_CR_PROCRST | RSTC_CR_PERRST;

    /* Waiting for reset */
    while(1);
}

/**
 *  \brief Handler for Sytem Tick interrupt.
 */
void SysTick_Handler( void )
{
    static int cnt = 0;

    /* Toggle LED per half second */
    cnt++;
    if ( (cnt % 500) == 0 )
    {
        cnt = 0;
        LED_Toggle(0);
    }
}

/**
 * \brief Application entry point.
 *
 * \return Unused (ANSI-C compatibility).
 */
extern int main( void )
{

    uint8_t c ;

    /* Disable watchdog */
    WDT_Disable( WDT ) ;

    /* Output example information */
    printf( "\n\r" ) ;
    printf( "EEFC Dual Bank Programming Example %s --\n\r", SOFTPACK_VERSION ) ;
    printf( "%s\n\r", BOARD_NAME ) ;
    printf( "Compiled: %s %s --\n\r", __DATE__, __TIME__ ) ;

    /* Configure systick for 1 ms. */
    printf( "Configure system tick to get 1ms tick period.\n\r" ) ;
    if ( SysTick_Config( BOARD_MCK / 1000 ) )
    {
        printf("-F- Systick configuration error\n\r" ) ;
    }

    /* Configure LED. */
    LED_Configure( 0 ) ;

    /* User input loop */
    while (1)
    {
        /* Display menu */
        DisplayMenu();

        /* Wait for user input */
        c = UART_GetChar();

        /* Reset timer */
        if ( c == 'b')
        {
            DemoFlashBasicOperation();
        }
        else if ( c == 'd')
        {
            DemoFirmwareUpdate();
        }
    }
}

