/****************************************************************************//**
  \file flashLoader.c

  \brief Implementation of internal flash loader.

  \author
    Atmel Corporation: http://www.atmel.com \n
    Support email: avr@atmel.com

  Copyright (c) 2008-2015, Atmel Corporation. All rights reserved.
  Licensed under Atmel's Limited License Agreement (BitCloudTM).

  \internal
  History:
    2/04/09 A. Khromykh - Created
*******************************************************************************/
/******************************************************************************
                   Includes section
******************************************************************************/
#include <types.h>
#include <bootStructure.h>
#include <sp_driver.h>
#include <flashLoader.h>

/******************************************************************************
                   Types section
******************************************************************************/
typedef union
{
  uint8_t  byteData[2];
  uint16_t wordData;
} FlashWord_t;

/******************************************************************************
                   Prototypes section
******************************************************************************/
static void flashLoadPageBuffer(uint16_t tableAddress, uint8_t dataByte);
static void flashEraseWritePage(uint16_t pageAddress);

/******************************************************************************
                   External global variables section
******************************************************************************/
extern BootBuffer_t dataBuffer;

/******************************************************************************
                   Global variables section
******************************************************************************/
static uint16_t currentPage = 0;
static FlashWord_t  data;

/******************************************************************************
                    Implementation section
******************************************************************************/
/**************************************************************************//**
\brief Function to load one word into page buffer.

\param[in]
  tableAddress - address in buffer to write the word.
\param[in]
  dataByte     - the byte of the word to load.
******************************************************************************/
static void flashLoadPageBuffer(uint16_t tableAddress, uint8_t dataByte)
{
  if (tableAddress & 0x01)
  {
    data.byteData[1] = dataByte;
    // Perform word load.
    SP_LoadFlashWord(tableAddress-1, data.wordData);
    // Wait for SPM to finish.
    flashWaitForSPM();
  }
  else
  {
    data.byteData[0] = dataByte;
  }
}

/**************************************************************************//**
\brief Function to do an erase-write on one page in the flash.

\note The maximum pageAddress must not be exceeded. The maximum number of
      pages can be found in the datasheet.

\param[in] pageAddress Page address to the page to erase/write.
******************************************************************************/
static void flashEraseWritePage(uint16_t pageAddress)
{
  bool sleepEnable = false;
  // Calculate actual start address of the page.
  uint32_t tableAddress = (uint32_t)pageAddress * (uint32_t)FLASH_PAGE_SIZE;

#if defined(RFR2_SPM)
  // Check for NRWW section.
  // When erasing or writing a page located inside the NRWW section, the CPU is halted. This makes device to indefinite sleep. 
  // So, check is added to avoid indefinite sleep
  if (tableAddress >= NRWW_FLASH_START_ADDRESS)
    sleepEnable = false;
  else
    sleepEnable = true;
#endif

  // Perform page erase.
  SP_EraseApplicationPage(tableAddress);
  // Wait for SPM to finish.
  flashSPMBusyWaitOrInterrupt(sleepEnable);
  // Perform page write.
  SP_WriteApplicationPage(tableAddress);
  // Wait for SPM to finish.
  flashSPMBusyWaitOrInterrupt(sleepEnable);
  // enable rww section
  SP_RWWSectionEnable();
  // Wait for SPM to finish.
  flashWaitForSPM();
}

/**************************************************************************//**
\brief Load received date to the flash.
******************************************************************************/
void flashPacketToPage(void)
{
  uint16_t packetPage;
  uint16_t tableAddress;
  uint8_t *dataPointer;

  dataPointer = dataBuffer.data;
  // Data size is added to even value to the write word data to the temporary buffer.
  if (dataBuffer.dataSize & 0x01) // check data size is odd or not
  {
    dataBuffer.data[dataBuffer.dataSize] = 0xFF; // fake byte for odd srec record
    dataBuffer.dataSize++;
  }

  while (dataBuffer.dataSize)
  {
    packetPage = dataBuffer.address >> PAGE_NUMBER_SHIFT;

    if (currentPage != packetPage)
    {
      // Do a Erase-Write of the page.
      flashEraseWritePage(currentPage);
      currentPage = packetPage;
    }

    tableAddress = dataBuffer.address & PAGE_POSITION_OFFSET_MASK;
    flashLoadPageBuffer(tableAddress, *dataPointer++);
    dataBuffer.address++;
    dataBuffer.dataSize--;
  }
}

/**************************************************************************//**
\brief Flush unloaded data before exit from bootloader.
******************************************************************************/
void flashFlushPage(void)
{
  // Do a Erase-Write of the page.
  flashEraseWritePage(currentPage);
}

#if defined(RFR2_SPM)
/******************************************************************************
Interrupt handler.
******************************************************************************/
ISR(SPM_READY_vect)
{
  SPMCSR &= ~(1 << SPMIE);
}
#endif
// eof flashLoader.c
