/**********************************************************************
* $Id$      lpc_iap.c           2011-11-21
*//**
* @file     lpc_iap.c
 * @brief   Contains all functions support for IAP on LPC
* @version  1.0
* @date     21. November. 2011
* @author   NXP MCU SW Application Team
* 
* Copyright(C) 2011, NXP Semiconductor
* All rights reserved.
*
***********************************************************************
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* products. This software is supplied "AS IS" without any warranties.
* NXP Semiconductors assumes no responsibility or liability for the
* use of the software, conveys no license or title under any patent,
* copyright, or mask work right to the product. NXP Semiconductors
* reserves the right to make changes in the software without
* notification. NXP Semiconductors also make no representation or
* warranty that such application will be suitable for the specified
* use without further testing or modification.
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors'
* relevant copyright in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers.  This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
**********************************************************************/
#ifdef __BUILD_WITH_EXAMPLE__
#include "lpc_libcfg.h"
#else
#include "lpc_libcfg_default.h"
#endif /* __BUILD_WITH_EXAMPLE__ */
#ifdef _IAP
#include "lpc_iap.h"
#include "lpc_clkpwr.h"

//  IAP Command
typedef void (*IAP)(uint32_t *cmd,uint32_t *result);
IAP iap_entry = (IAP) IAP_LOCATION;
#define IAP_Call    iap_entry

/** @addtogroup IAP_Public_Functions LCD Public Function
 * @ingroup IAP
 * @{
 */
 

/*********************************************************************//**
 * @brief     Get Sector Number
 *
 * @param[in] adr      Sector Address
 *
 * @return    Sector Number.
 *
 **********************************************************************/
 uint32_t GetSecNum (uint32_t adr)
{
    uint32_t n;

    n = adr >> 12;                               //  4kB Sector
    if (n >= 0x10) {
      n = 0x0E + (n >> 3);                       // 32kB Sector
    } 

    return (n);                                  // Sector Number
}

/*********************************************************************//**
 * @brief     Prepare sector(s) for write operation
 *
 * @param[in] start_sec           The number of start sector
 * @param[in] end_sec             The number of end sector
 *
 * @return    CMD_SUCCESS/BUSY/INVALID_SECTOR.
 *
 **********************************************************************/
IAP_STATUS_CODE PrepareSector(uint32_t start_sec, uint32_t end_sec)
{
    IAP_COMMAND_Type command;
    command.cmd    = IAP_PREPARE;                    // Prepare Sector for Write
    command.param[0] = start_sec;                    // Start Sector
    command.param[1] = end_sec;                      // End Sector
    IAP_Call (&command.cmd, &command.status);        // Call IAP Command
    return (IAP_STATUS_CODE)command.status;
}

/*********************************************************************//**
 * @brief     Copy RAM to Flash
 *
 * @param[in] dest            destination buffer (in Flash memory).
 * @param[in] source       source buffer (in RAM).
 * @param[in] size            the write size.
 *
 * @return    CMD_SUCCESS.
 *                  SRC_ADDR_ERROR/DST_ADDR_ERROR
 *                  SRC_ADDR_NOT_MAPPED/DST_ADDR_NOT_MAPPED
 *                  COUNT_ERROR/SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION
 *                  BUSY
 *
 **********************************************************************/
IAP_STATUS_CODE CopyRAM2Flash(uint8_t * dest, uint8_t* source, IAP_WRITE_SIZE size)
{
    uint32_t sec;
    IAP_STATUS_CODE status;
    IAP_COMMAND_Type command;

    // Prepare sectors
    sec = GetSecNum((uint32_t)dest);
    status = PrepareSector(sec, sec);
    if(status != CMD_SUCCESS)
        return status;
   
    // write
    command.cmd    = IAP_COPY_RAM2FLASH;             // Copy RAM to Flash
    command.param[0] = (uint32_t)dest;                 // Destination Flash Address
    command.param[1] = (uint32_t)source;               // Source RAM Address
    command.param[2] =  size;                          // Number of bytes
    command.param[3] =  CLKPWR_GetCLK(CLKPWR_CLKTYPE_CPU) / 1000;         // CCLK in kHz
    IAP_Call (&command.cmd, &command.status);              // Call IAP Command
      
    return (IAP_STATUS_CODE)command.status;             // Finished without Errors    
}

/*********************************************************************//**
 * @brief     Erase sector(s)
 *
 * @param[in] start_sec    The number of start sector
 * @param[in] end_sec      The number of end sector
 *
 * @return    CMD_SUCCESS.
 *                  INVALID_SECTOR
 *                  SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION
 *                  BUSY
 *
 **********************************************************************/
IAP_STATUS_CODE EraseSector(uint32_t start_sec, uint32_t end_sec)
{
    IAP_COMMAND_Type command;
    IAP_STATUS_CODE status;

    // Prepare sectors
    status = PrepareSector(start_sec, end_sec);
    if(status != CMD_SUCCESS)
        return status;

    // Erase sectors
    command.cmd    = IAP_ERASE;                    // Prepare Sector for Write
    command.param[0] = start_sec;                  // Start Sector
    command.param[1] = end_sec;                    // End Sector
    command.param[2] =  CLKPWR_GetCLK(CLKPWR_CLKTYPE_CPU) / 1000;         // CCLK in kHz
    IAP_Call (&command.cmd, &command.status);      // Call IAP Command
    return (IAP_STATUS_CODE)command.status;  
}

/*********************************************************************//**
 * @brief         Blank check sector(s)
 *
 * @param[in]     start_sec    The number of start sector
 * @param[in]     end_sec      The number of end sector
 * @param[out]    first_nblank_loc  The offset of the first non-blank word
  * @param[out]   first_nblank_val  The value of the first non-blank word
 *
 * @return        CMD_SUCCESS.
 *                  INVALID_SECTOR
 *                  SECTOR_NOT_BLANK
 *                  BUSY
 *
 **********************************************************************/
IAP_STATUS_CODE BlankCheckSector(uint32_t start_sec, uint32_t end_sec,
                                 uint32_t *first_nblank_loc, 
                                 uint32_t *first_nblank_val)
{
    IAP_COMMAND_Type command;
    
    command.cmd    = IAP_BLANK_CHECK;                // Prepare Sector for Write
    command.param[0] = start_sec;                    // Start Sector
    command.param[1] = end_sec;                      // End Sector
    IAP_Call (&command.cmd, &command.status);        // Call IAP Command

    if(command.status == SECTOR_NOT_BLANK)
    {
      // Update out value
      if(first_nblank_loc != NULL)
          *first_nblank_loc =  command.result[0];
      if(first_nblank_val != NULL)
          *first_nblank_val =  command.result[1];
    }

    return (IAP_STATUS_CODE)command.status;
}

/*********************************************************************//**
 * @brief      Read part identification number
 *
 * @param[out] partID  Part ID
 *
 * @return     CMD_SUCCESS
 *
 **********************************************************************/
IAP_STATUS_CODE ReadPartID(uint32_t *partID)
{
   IAP_COMMAND_Type command;
   command.cmd = IAP_READ_PART_ID;
   IAP_Call (&command.cmd, &command.status);        // Call IAP Command

   if(command.status == CMD_SUCCESS)
   {
      if(partID != NULL)
         *partID = command.result[0];
   }

   return (IAP_STATUS_CODE)command.status;
}

/*********************************************************************//**
 * @brief      Read boot code version. The version is interpreted as <major>.<minor>.
 *
 * @param[out] major  The major
 * @param[out] minor  The minor
 *
 * @return     CMD_SUCCESS
 *
 **********************************************************************/
IAP_STATUS_CODE ReadBootCodeVer(uint8_t *major, uint8_t* minor)
{
   IAP_COMMAND_Type command;
   command.cmd = IAP_READ_BOOT_VER;
   IAP_Call (&command.cmd, &command.status);        // Call IAP Command

   if(command.status == CMD_SUCCESS)
   {
      if(major != NULL)
         *major = (command.result[0] >> 8) & 0xFF;
      if(minor != NULL)
         *minor = (command.result[0]) & 0xFF;
   }

   return (IAP_STATUS_CODE)command.status;
}

/*********************************************************************//**
 * @brief      Read Device serial number.
 *
 * @param[out] uid   Serial number.
 *
 * @return     CMD_SUCCESS
 *
 **********************************************************************/
IAP_STATUS_CODE ReadDeviceSerialNum(uint32_t *uid)
{
   IAP_COMMAND_Type command;
   command.cmd = IAP_READ_SERIAL_NUMBER;
   IAP_Call (&command.cmd, &command.status);        // Call IAP Command

   if(command.status == CMD_SUCCESS)
   {
      if(uid != NULL)
      {
        uint32_t i = 0;
        for(i = 0; i < 4; i++)
           uid[i] =  command.result[i];
      }
   }

   return (IAP_STATUS_CODE)command.status;
}

/*********************************************************************//**
 * @brief     compare the memory contents at two locations.
 *
 * @param[in] addr1   The address of the 1st buffer (in RAM/Flash).
 * @param[in] addr2   The address of the 2nd buffer (in RAM/Flash).
 * @param[in] size    Number of bytes to be compared; should be a multiple of 4.
 *
 * @return    CMD_SUCCESS
 *                  COMPARE_ERROR
 *                  COUNT_ERROR (Byte count is not a multiple of 4)
 *                  ADDR_ERROR
 *                  ADDR_NOT_MAPPED
 *
 **********************************************************************/
IAP_STATUS_CODE Compare(uint8_t *addr1, uint8_t *addr2, uint32_t size)
{
   IAP_COMMAND_Type command;
   command.cmd = IAP_COMPARE;
   command.param[0] = (uint32_t)addr1;
   command.param[1] = (uint32_t)addr2;
   command.param[2] = size;
   IAP_Call (&command.cmd, &command.status);        // Call IAP Command

   return (IAP_STATUS_CODE)command.status;
}

/*********************************************************************//**
 * @brief     Re-invoke ISP.
 *
 * @param[in] None.
 *
 * @return    None.
 *
 **********************************************************************/
void InvokeISP(void)
{
   IAP_COMMAND_Type command;
   command.cmd = IAP_REINVOKE_ISP;
   IAP_Call (&command.cmd, &command.status);        // Call IAP Command
}

/**
 * @}
 */
 

#endif /*_IAP*/
