/**************************************************************************************************
  Filename:       FlashUtils.c
  Revised:        $Date: 2007-11-04 21:21:01 -0800 (Sun, 04 Nov 2007) $
  Revision:       $Revision: 15860 $

  Description:    Describe the purpose and contents of the file.


  Copyright 2006-2007 Texas Instruments Incorporated. All rights reserved.

  IMPORTANT: Your use of this Software is limited to those specific rights
  granted under the terms of a software license agreement between the user
  who downloaded the software, his/her employer (which must be your employer)
  and Texas Instruments Incorporated (the "License").  You may not use this
  Software unless you agree to abide by the terms of the License. The License
  limits your use, and you acknowledge, that the Software may not be modified,
  copied or distributed unless embedded on a Texas Instruments microcontroller
  or used solely and exclusively in conjunction with a Texas Instruments radio
  frequency transceiver, which is integrated into your product.  Other than for
  the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  works of, modify, distribute, perform, display or sell this Software and/or
  its documentation for any purpose.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, 
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

  Should you have any questions regarding your right to use this Software,
  contact Texas Instruments Incorporated at www.TI.com. 
**************************************************************************************************/

#define ENABLE_BIT_DEFINITIONS 1
#include "zcomdef.h"
#include "FlashUtils.h"
#include <ioCC2430.h>
#include "OADSupport.h"
#include "OSAL_Memory.h"

#ifdef NV_IS_I2C
#include "i2cSupport.h"
#endif

#ifdef NV_IS_SPI
#include "serialize.h"
#endif

#define STATIC static


#define FERASE  0x01  // Page erase: erase=1
#define FWRITE  0x02  // Page write: write=1
#define FCONRD  0x10  // Continuous read: enable=1
#define FSWBSY  0x40  // Single write: busy=1
#define FBUSY   0x80  // Write/erase: busy=1
#define FWBUSY  0xC0  // Flash write: busy=1

/******************************************************************************
 ** ANY VARIABLES DECLARED 'STATIC' MUST BE USED _ONLY_ IN BOOT STARTUP CODE
 ** memory addresses assigned to static variables will not be valid when
 ** application code is running. they are valid only when the boot startup
 ** code is running.
*******************************************************************************/

/*********************************************************************
 * MACROS
 */
//Macro for erasing a given flash page
#define ERASE           0x01
#define FLASH_ERASE_PAGE(page) \
   do{                         \
      FADDRH = (page) << 1;    \
      FCTL = ERASE;            \
      asm("NOP");              \
      while(FCTL == 0x80);     \
   }while (0)

// DMA Sturcture
typedef struct
{
  uint8  SRC_HI;
  uint8  SRC_LO;
  uint8  DST_HI;
  uint8  DST_LO;
  uint8  VLEN;
  uint8  LEN;
  uint8  TRIG;
  uint8  INCMODE;
} DMA_t;

static DMA_t  __xdata FlashDMA;

extern __near_func  uint8  GetCodeByte(uint32 address);

// set up the mailbox in RAM
extern mbox_t mbox;


// LOCAL FUNCTIONS
STATIC  uint8  CheckCode(image_t, uint32, uint32);
STATIC  uint8  GetPreamble(image_t, uint32, preamble_t *);
STATIC  uint32 CRC32Value(uint16);
STATIC  uint8  imageCRC(image_t, uint32, uint32, preamble_t *);
STATIC  uint8  AccessStatus(action_t, sfield_t, void *);
STATIC  uint8  ReadFlash(image_t, uint32 page, uint8 __xdata *buf, uint16 len);
STATIC  uint8  WriteFlash(image_t, uint32 page, uint8 __xdata *buf, uint16 len);

__near_func void   flashErasePage(uint8, uint8 __xdata *);

// EXTERNAL FUNCTIONS
__near_func void  halFlashDmaTrigger(void);

static int8 (*rw_Xmem)(uint8, uint32, uint8 *, uint16);

void FlashInit()
{
#ifdef NV_IS_SPI
    // initialize SPI-dataflash interface
    DF_spiInit(&rw_Xmem);
#endif

#ifdef NV_IS_I2C
    // initialize I2C-dataflash interface
    DF_i2cInit(&rw_Xmem);
#endif

     // populate mailbox function pointers
    mbox.ReadFlash         = ReadFlash;
    mbox.WriteFlash        = WriteFlash;
    mbox.CheckCodeSanity   = CheckCode;
    mbox.GetPreamble       = GetPreamble;
    mbox.AccessZLOADStatus = AccessStatus;

    return;
}

void GetFlashRWFunc(int8 (**func)(uint8, uint32, uint8 *, uint16))
{
#ifdef NV_IS_SPI
    // initialize SPI-dataflash interface
    DF_spiInit(&rw_Xmem);
#endif

#ifdef NV_IS_I2C
    // initialize I2C-dataflash interface
    DF_i2cInit(&rw_Xmem);
#endif

  *func = rw_Xmem;
}

/************************************************************************************
 *   uint8 CheckCode
 *      Purpose: Do all code sanity checks. Currently, this is to check the magic
 *               and check the FCS.
 *
 *        This routine is avaliable to application code. It is re-entrant but not
 *        thread-safe.
 *
 *      Arguments:
 *        input:
 *          baseAddr: base address of image
 *          firstPageAddr: address of first page
 *        output:
 *          none.
 *      Returns:
 *        0: Code at spefied address is sane
 *        1: Code at spefied address is NOT sane
 ************************************************************************************/
STATIC  uint8 CheckCode(image_t itype, uint32 baseAddr, uint32 firstPageAddress)
{
    uint8      rc;
    preamble_t preamble;

    // get preamble
    mbox.GetPreamble(itype, firstPageAddress, &preamble);

    // check for magic
    if ((preamble.pre_Magic[0] != PREAMBLE_MAGIC1) || (preamble.pre_Magic[1] != PREAMBLE_MAGIC2))  {
        return 1;
    }

    // only thing left to check for is CRC
    rc = imageCRC(itype, baseAddr, firstPageAddress, &preamble);

    return rc;
}

/************************************************************************************
 *   uint8 imageCRC
 *      Purpose: compute image CRC. Taken as an amalgam from various sources. There
 *      are a number of slightly varying implementations. It was hard to find test
 *      data for this specific implementation. The test used is the very weak test
 *      in which the ASCII string "123456789" is checked.
 *
 *      In this space it is calculating the CRC on downloaded image only. The
 *      application will never request the CRC calculated on the active image.
 *
 *      Note: the routine that follows this one also supports the CRC calculation.
 *
 *      Arguments:
 *        input:
 *          baseAddr: base address of image
 *          firstPageOffset: offset from baseAddr of actual first page of image
 *          premable: pointer to preamble info
 *        output:
 *          none
 *      Returns:
 *        0: CRC calculation matches the CRC in image
 *        1: CRC calculation does NOT match the CRC in image
 ************************************************************************************/
#define  DFBUFSIZE  32
STATIC  uint8 imageCRC(image_t itype, uint32 baseAddr, uint32 firstPageAddress, preamble_t *preamble)
{
    uint8  ch, scnt, dfIdx, dfSize;
    uint32 imgCRC, imglen, ulCRC, ulTemp1, ulTemp2, lastAddress, baseOrig;
#ifdef CC2430_BOOT_CODE
    uint8 cptr[DFBUFSIZE];
#else
    uint8 *cptr;
#endif

    imglen = preamble->pre_Length;

    // figure out wrap address in case image wraps within download area
    // the number is too big if the image doesn't wrap but that won't
    // matter becuase the loop(s) below will terminate since the image
    // length count is reached. this value matters only if the image
    // stored actually wraps.
    lastAddress = (baseAddr + imglen - 1) | ((itype == IMAGE_DL) ? (DF_PAGESIZE-1) : (CHIP_PAGESIZE-1));
    baseOrig    = baseAddr;
    baseAddr    = firstPageAddress;

    // start with all bits on. common practice to guard against initial sequence of
    // 0's (even though we don't have this case)
    ulCRC  = 0xFFFFFFFF;
    imgCRC = 0;
    scnt   = 0;

    dfIdx = 0;
    if (IMAGE_DL == itype)  {
      dfIdx = dfSize = DFBUFSIZE;
#ifndef CC2430_BOOT_CODE
      if (!(cptr = osal_mem_alloc(DFBUFSIZE)))  {
      // Plan B if the malloc fails...
        dfIdx = dfSize = 1;
        cptr           = &ch;
      }
#endif
    }

    // the same logic is used to both calculate the checksum and to retrieve stored
    // CRC. this is because the method below already deals with wrapping so we
    // might as well use the same logic to get the CRC. Otherwise the wrapping
    // logic has to be repeated: though unlikely, the CRC _could_ span the wrap.
    do  {
        for (; baseAddr <= lastAddress && imglen; baseAddr++, imglen--)  {
            // next byte...
          if (IMAGE_ACTIVE == itype)  {
            ch = GetCodeByte(baseAddr);
          }
          else  {
            if (dfIdx >= dfSize)  {
              rw_Xmem(XMEM_READ, baseAddr, cptr, dfSize);
              dfIdx = 0;
            }
            ch = cptr[dfIdx++];
          }
          if (imglen > FCS_LENGTH)  {
              ulTemp1 = (ulCRC >> 8) & 0x00FFFFFFL;
              ulTemp2 = CRC32Value(((uint16)ulCRC ^ ch) & 0xFF);
              ulCRC   = ulTemp1 ^ ulTemp2;
          }
          else  {
              // we've reached the stored CRC. create the actual
              // value by shifting in the bytes.
              imgCRC = imgCRC | (uint32)ch<<scnt;
              scnt  += 8;
          }
        }
        baseAddr = baseOrig;
    } while (imglen);

#ifndef CC2430_BOOT_CODE
    if (dfSize > 1)  {
      osal_mem_free(cptr);
    }
#endif

    // XOR with all bits on. this is a common practice
    ulCRC ^= 0xFFFFFFFF;

    // match?
    if (ulCRC != imgCRC)  {
        // "Close, but no match and the board goes back" (remember that?)
        return 1;
    }

    return 0;
}

STATIC  uint32 CRC32Value(uint16 inCRC)
{
    int16  j;
    uint32 ulCRC = inCRC;

    // for this byte (inCRC)...
    for (j=8; j; j--) {
        // lsb on? yes -- shift right and XOR with poly. else just shift
        if (ulCRC & 1) {
            ulCRC = (ulCRC >> 1) ^ CRC32_POLYNOMIAL;
        }
        else  {
            ulCRC >>= 1;
        }
    }

    return ulCRC;
}

/************************************************************************************
 *   uint8 GetPreamble
 *      Purpose: get image preamble
 *
 *        This routine is avaliable to application code. It is re-entrant but not
 *        thread-safe.
 *
 *      Arguments:
 *        input:
 *          baseAddr: base address of image
 *        output:
 *          info: void pointer to destination memory
 *      Returns:
 *        0: OK
 *        1: error
 *
 *   NOTE: this works only because the preamble is always on the first flash page.
 *         This is important given that the downloaded image can wrap. It won't wrap
 *         within a page.
 ************************************************************************************/
STATIC uint8 GetPreamble(image_t itype, uint32 baseAddr, preamble_t *info)
{
    uint8 preOffset;

    if (IMAGE_DL == itype)  {

        mbox.AccessZLOADStatus(ACTION_GET, SFIELD_PREAMBLE_OFFSET, &preOffset);
        baseAddr += preOffset;
        rw_Xmem(XMEM_READ, baseAddr, (uint8 *)info, sizeof(preamble_t));
    }
    else if (IMAGE_ACTIVE == itype) {
        uint8 i;

        baseAddr += PREAMBLE_BASE;

        // need > 16-bit pointer arithmetic
        for (i=0; i<sizeof(preamble_t); ++i, ++baseAddr)  {
            *((uint8 *)info+i) = GetCodeByte(baseAddr);
        }
    }
    else  {
      return 1;
    }

    return 0;
}


/************************************************************************************
 *   uint8 AccessStatus
 *      Purpose: get and set fields in OAD persistent memory
 *
 *        This routine is avaliable to application code. It neither re-entrant nor
 *        thread-safe.
 *
 *      Arguments:
 *        input:
 *          action: get or set
 *          sfield: which field to operate on
 *        output:
 *          info: void pointer to source (set) or destination (get) memory
 *      Returns:
 *        0: OK
 *        1: bad action or field
 ************************************************************************************/
STATIC  uint8 AccessStatus(action_t action, sfield_t sfield, void *info)
{
    uint8   bcnt, os, rc = 0;

    // all results for this function (except the boot sector size) are byte copies
    // the offset and byte counts are set based on boot code knowledge of how the
    // persistent memory is arranged. the offsets and sizes are set here. they are
    // defined in a header. they could be calculated but it takes a lot more code.
    switch (sfield)  {
#if 0   // base and first page always the same for the 8051 with external memory
        case SFIELD_DLFIRSTPAGE_ADDR:
            bcnt = sizeof(uint32);
            os   = OADPM_OS_FIRSTPAGE_ADDR;
            break;
#else
        case SFIELD_DLFIRSTPAGE_ADDR:
#endif
        case SFIELD_DLBASE:
            bcnt = sizeof(uint32);
            os   = OADPM_OS_BADDR;
            break;

        case SFIELD_PREAMBLE_OFFSET:
            bcnt = sizeof(uint8);
            os   = OADPM_OS_PREAMBLE_OFFSET;
            break;

            // we don't need to support the next few accesses now. they were intended
            // to support the case where the entire image could not be downloaded and
            // we needed to have server context in case of failure. they have been
            // tested but are removed for now.
#if 0
        case SFIELD_SERVER_IEEE_ADDR:
            bcnt = SIZEOF_IEEE_ADDRESS;
            os   = OADPM_OS_IEEE_ADDR;
            break;

        case SFIELD_SERVER_NWK_ADDR:
            bcnt = sizeof(uint16);
            os   = OADPM_OS_NWK_ADDR;
            break;

        case SFIELD_SERVER_ENDPOINT:
            bcnt = sizeof(uint8);
            os   = OADPM_OS_ENDPT;
            break;

        case SFIELD_IMAGE_ID:
            bcnt = SIZEOF_DLIMAGE_INFO;
            os   = OADPM_OS_DLIMG_INFO;
            break;
#endif
        case SFIELD_STATUS:
            bcnt = sizeof(uint16);
            os   = OADPM_OS_STATUS;
            break;

#if 0  // not used for CC2430
        case SFIELD_BOOTSIZE:
            // doesn't really belong here because it isn't kept in PM but
            // it is conventient to support it with this API
            if (action == ACTION_SET)  {
                // not legal
                return 1;
            }
            *(uint16 *)info = bootSize;
            return 0;
#endif
        case SFIELD_EXTERNAL_MEM_OS:
            // doesn't really belong here because it isn't kept in PM but
            // it is conventient to support it with this API
            if (action == ACTION_SET)  {
                // not legal
                return 1;
            }
            *(uint16 *)info = DL_BASE_ADDRESS;
            return 0;

        default:
            rc = 1;
            break;
    }

    if (!rc)  {
        if (action == ACTION_SET)  {
          rw_Xmem(XMEM_WRITE, SYSNV_BASE_ADDRESS + os, info, bcnt);
        }
        else if (action == ACTION_GET)  {
          rw_Xmem(XMEM_READ, SYSNV_BASE_ADDRESS + os, info, bcnt);
        }
        else  {
            rc = 1;
        }
    }

    return rc;
}

STATIC  uint8 ReadFlash(image_t itype, uint32 address, uint8 __xdata *buf, uint16 len)
{
  uint8 rc = 0;

  if (IMAGE_ACTIVE == itype)  {
    uint16 i = len;

    while (i--)  {
      *buf++ = GetCodeByte(address++);
    }
  }
  else if (IMAGE_DL == itype)  {
    rw_Xmem(XMEM_READ, address, buf, len);
  }
  else  {
    rc = 1;
  }

  return rc;
}

STATIC  uint8 WriteFlash(image_t itype, uint32 address, uint8 __xdata *buf, uint16 len)
{
  uint8 rc = 1;  // 1 == write was OK.

  if (IMAGE_ACTIVE == itype)  {
    uint16  fbase, i;
    uint8   page = address >> CHIP_SHIFTCOUNT;

    // if we're on a page boundary for the target (chip) flash, erase the page
    // this implies that we either write a page sequentially or minimally
    // write the first address on the first call so the page gets eraseed.
    if (!(address & 0x000007FF))  {
      FLASH_ERASE_PAGE(page);
    }


    // OK. do the rest. use DMA Channel 0
    // Set up Flash DMA

    FlashDMA.DST_HI  = 0xdf;
    FlashDMA.DST_LO  = 0xaf;
    FlashDMA.VLEN    = 0x00;
    FlashDMA.LEN     = 0x04;
    FlashDMA.TRIG    = 0x12;
    FlashDMA.INCMODE = 0x42;

    DMA0CFGH = (uint16)&FlashDMA >> 8;
    DMA0CFGL = (uint16)&FlashDMA;

    fbase = (page << 9) + ((address & 0x000007FF)>>2);

    // 'len' must be 0 (mod 4)
    for (i=0; i<(len/4); ++i)  {
      // set buffer pointer in DMA config info
      FlashDMA.SRC_HI  = (uint16)buf >> 8;
      FlashDMA.SRC_LO  = (uint16)buf;
      // Set Flash write address based on fbase
      FADDRH = fbase >> 8;
      FADDRL = fbase;

      DMAARM = 0x01;
      halFlashDmaTrigger();
      while (!(DMAIRQ & 0x01));   // wait for DMA transfer
      while ( FCTL & FWBUSY );  // wait until Flash controller not busy
      DMAIRQ &= ~0x01;
      fbase++;
      buf += 4;
    }
  }
  else if (IMAGE_DL == itype)  {
    rw_Xmem(XMEM_WRITE, address, buf, len);
  }
  else  {
    rc = 0;
  }

  return rc;
}


