/*
 * @brief CRC32 check of image code
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2014
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights 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.
 */

#include "sl_common.h"

/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/

/* Address of CRC length field and CRC value */
#define SL_CRCLEN_ADDR1  (&pImageHeader1->crc_len)
#define SL_CRCVAL_ADDR1  (&pImageHeader1->crc_value)

#define SL_CRCLEN_ADDR2  (&pImageHeader2->crc_len)
#define SL_CRCVAL_ADDR2  (&pImageHeader2->crc_value)

#define ENDOFFLASH          0x0008000

/* Initial value won't damage other CRC computation code using the
   CRC generation function */
static uint32_t *pValCRC = (uint32_t *) 0xFFFFFFFF;

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/

/*****************************************************************************
 * Private functions
 ****************************************************************************/

/*****************************************************************************
 * Public functions
 ****************************************************************************/

/* Compute CRC32 for the image programmed in flash. */
uint32_t computeAppCRC(uint8_t app_number)
{
	uint32_t SL_BOOTAPP_ADDR;
	uint32_t *pLenCRC;
	if(app_number==1)
	{
		SL_BOOTAPP_ADDR=SL_BOOTAPP_ADDR1;
		pLenCRC = ( uint32_t *) SL_CRCLEN_ADDR1;
	}
	else
	{
		SL_BOOTAPP_ADDR=SL_BOOTAPP_ADDR2;
		pLenCRC = ( uint32_t *) SL_CRCLEN_ADDR2;
	}
	
	uint32_t i, crc, len = *pLenCRC;
	uint32_t *pDataCRC;

	/* Sanity checks */
	if ((len == 0) || (len > (ENDOFFLASH - SL_BOOTAPP_ADDR))) {
		return len;
	}

	/* Initialize CRC engine */
	Chip_CRC_Init();

	/* Setup for CRC32 */
	Chip_CRC_UseCRC32();

	/* CRC32 computation in words, skipping CRC len and val fields */
	pDataCRC = (uint32_t *) SL_BOOTAPP_ADDR;
	for (i = 0; i < len; i += 4) {
		/* Skip CRC32 value field */
		if (pDataCRC != pValCRC) {
			Chip_CRC_Write32(*pDataCRC);
		}

		pDataCRC++;
	}

	/* Get computed CRC value */
	crc = Chip_CRC_Sum();

	/* Restore original CRC clocking to off */
	Chip_CRC_Deinit();

	return crc;
}

/* Verify that application's CRC is correct. Used to determine if a valid
   application is located in FLASH before jumping to it. */
uint32_t checkAppCRC(uint8_t app_number)
{
	/* Set CRC skip address, skip CRC32 value since it's not part of the CRC */
	uint32_t crc = 0;
	if (app_number==1)
		pValCRC = (uint32_t *) SL_CRCVAL_ADDR1;
	else 
		pValCRC = (uint32_t *) SL_CRCVAL_ADDR2;

	/* compute app CRC */
	crc = computeAppCRC(app_number);

	/* Compare */
	if (*pValCRC == crc) {
		crc = 0;
	}

	/* SO it won't interfere with CRC generation later */
	pValCRC = NULL;

	return crc;
}

uint32_t computeCrc32(uint32_t *data, uint32_t len)
{
	uint32_t i, crc;
	/* Initialize CRC engine */
	Chip_CRC_Init();

	/* Setup for CRC32 */
	Chip_CRC_UseCRC32();

	/* CRC32 computation in words */
	for (i = 0; i < (len >> 2); i++) {
		Chip_CRC_Write32(data[i]);
	}

	/* Get computed CRC value */
	crc = Chip_CRC_Sum();

	/* Restore original CRC clocking to off */
	Chip_CRC_Deinit();

	return crc;
}
