/*******************************************************************************
* NXP Semiconductors
* (c) Copyright 2016 NXP Semiconductors
* ALL RIGHTS RESERVED.
********************************************************************************
Services performed by NXP in this matter are performed AS IS and without any 
warranty. CUSTOMER retains the final decision relative to the total design
and functionality of the end product. NXP neither guarantees nor will be held
liable by CUSTOMER for the success of this project.
NXP DISCLAIMS ALL WARRANTIES, EXPRESSED, IMPLIED OR STATUTORY INCLUDING,
BUT NOT LIMITED TO, IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
A PARTICULAR PURPOSE ON ANY HARDWARE, SOFTWARE ORE ADVISE SUPPLIED 
TO THE PROJECT BY NXP, AND OR NAY PRODUCT RESULTING FROM NXP SERVICES. 
IN NO EVENT SHALL NXP BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THIS AGREEMENT. 
CUSTOMER agrees to hold NXP harmless against any and all claims demands 
or actions by anyone on account of any damage, or injury, whether commercial,
contractual, or tortuous, rising directly or indirectly as a result 
of the advise or assistance supplied CUSTOMER in connection with product, 
services or goods supplied under this Agreement.
********************************************************************************
* File:             main.c
* Owner:            Lukas Zadrapa
* Version:          1.0
* Date:             Jun-20-2016
* Classification:   General Business Information
* Brief:            Serial bootloader for MPC5748G
********************************************************************************
* Detailed Description:
*
* Serial bootloader for MPC5748G
*
* ------------------------------------------------------------------------------
* Test HW:         MPC574XG-324DS Rev.A + MPC574XG-MB Rev.C
* MCU:             PPC5748GMMN6A 1N81M
* Fsys:            160 MHz PLL
* Debugger:        Lauterbach Trace32
* Target:          internal_FLASH
* Terminal:        115200bps, no parity, 1 stop bit, XOn-XOff flow control
                   on LINFlexD_2
********************************************************************************
Revision History:
Ver  Date         Author            Description of Changes
1.0  Jun-20-2016  Lukas Zadrapa     Initial version
*******************************************************************************/

/*******************************************************************************
* Includes                                                                     
*******************************************************************************/
#include "MPC5748G_310.h"
#include "typedefs.h"
#include "ssd_c55.h"
#include "serial_communication.h"
#include "INTC_Init.h"
#include "Errors.h"
#include "Srec.h"

/*******************************************************************************
* Constants and macros
*******************************************************************************/
#define DRUN_MODE	0x3

#define pFlashInit		((PFLASHINIT)FlashInit_C)
#define pFlashErase 		((PFLASHERASE)FlashErase_C)
#define pFlashEraseAlternate 	((PFLASHERASEALTERNATE)FlashEraseAlternate_C)
#define pBlankCheck		((PBLANKCHECK)BlankCheck_C)
#define pFlashProgram  		((PFLASHPROGRAM)FlashProgram_C)
#define pFlashCheckStatus	((PFLASHCHECKSTATUS)FlashCheckStatus_C)
#define pProgramVerify 		((PPROGRAMVERIFY) ProgramVerify_C)
#define pGetLock       		((PGETLOCK)GetLock_C)
#define pSetLock        	((PSETLOCK)SetLock_C)
#define pCheckSum		((PCHECKSUM)CheckSum_C)
#define pFlashSuspend		((PFLASHSUSPEND)FlashSuspend_C)
#define pFlashResume		((PFLASHRESUME)FlashResume_C)
#define pUserMarginReadCheck		((PUSERMARGINREADCHECK)UserMarginReadCheck_C)
#define pFlashArrayIntegrityCheck	((PFLASHARRAYINTEGRITYCHECK)FlashArrayIntegrityCheck_C)
#define pFlashArrayIntegrityResume	((PFLASHARRAYINTEGRITYRESUME)FlashArrayIntegrityResume_C)
#define pFlashArrayIntegritySuspend	((PFLASHARRAYINTEGRITYSUSPEND)FlashArrayIntegritySuspend_C)
#define pOverPgmProtGetStatus	((POVERPGMPROTGETSTATUS)OverPgmProtGetStatus_C)
#define pFlashAbort		((PFLASHABORT)FlashAbort_C)

#define C55_REG_BASE		0xFFFE0000
#define MAIN_ARRAY_BASE		0x00404000
#define UTEST_ARRAY_BASE	0x00400000
#define C55_PROGRAMMABLE_SIZE	0x80

/*******************************************************************************
* External objects
*******************************************************************************/
extern const unsigned long FlashInit_C[];
extern const unsigned long FlashErase_C[];
extern const unsigned long FlashEraseAlternate_C[];
extern const unsigned long BlankCheck_C[];
extern const unsigned long FlashProgram_C[];
extern const unsigned long ProgramVerify_C[];
extern const unsigned long FlashCheckStatus_C[];
extern const unsigned long GetLock_C[];
extern const unsigned long SetLock_C[];
extern const unsigned long CheckSum_C[];
extern const unsigned long FlashSuspend_C[];
extern const unsigned long FlashResume_C[];
extern const unsigned long UserMarginReadCheck_C[];
extern const unsigned long FlashArrayIntegrityCheck_C[];
extern const unsigned long FlashArrayIntegrityResume_C[];
extern const unsigned long FlashArrayIntegritySuspend_C[];
extern const unsigned long OverPgmProtGetStatus_C[];
extern const unsigned long FlashAbort_C[];

/*******************************************************************************
* Global variables
*******************************************************************************/
SSD_CONFIG ssdConfig = {
    C55_REG_BASE,         /* C55 control register base */
    MAIN_ARRAY_BASE,      /* base of main array */
    { 0, 0, 0 },          /* blocks info of low address space */
    { 0, 0, 0 },          /* blocks info of mid address space */
    { 0, 0, 0 },          /* blocks info of high address space */
    0,                    /* number of blocks in 256K address space */
    UTEST_ARRAY_BASE,     /* base of UTEST array */
    TRUE,                 /* interface flag indicate main or alternate interface */
    C55_PROGRAMMABLE_SIZE,/* programmable size */
    FALSE                 /* debug mode selection */
};

UINT32 returnCode = 0;	  /* Return code from each SSD function. */
CONTEXT_DATA CtxData;

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

/*******************************************************************************
Function Name : Sys_Init
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : Clock settings - configure 160MHz system clock using
                40MHz crystal
Issues        : NONE
*******************************************************************************/
void Sys_Init(void)
{    
    /* enable all modes, enable all peripherals */
    MC_ME.ME.R        = 0x000005FF;
    MC_ME.RUN_PC[0].R = 0x000000FE;
    /* Connect XOSC to PLL */
    MC_CGM.AC5_SC.B.SELCTL = 1;              
    /* Configure PLL0 Dividers - 160MHz from 40Mhx XOSC */
    PLLDIG.PLLDV.B.PREDIV  = 2;
    PLLDIG.PLLDV.B.MFD     = 32;
    PLLDIG.PLLDV.B.RFDPHI  = 1;
    PLLDIG.PLLCAL3.R       = 0x09C3C000;
    PLLDIG.PLLFD.B.SMDEN   = 1;       /* Sigma delta modulation disabled */
    /* switch to PLL */
    MC_ME.DRUN_MC.R  = 0x00130172;    /* FLASH in normal mode, PLLON, FXOSC ON, Use PLL_PHI_0 */
    
    MC_ME.MCTL.R     = 0x30005AF0;    /* DRUN target mode + KEY */
    MC_ME.MCTL.R     = 0x3000A50F;    /* DRUN target mode + KEY Inverted */
    
    while(MC_ME.GS.B.S_MTRANS == 1); /* Wait for mode transition complete */

    /* Enable CLKOUT signal */
    /* System clock (160MHz divided by 20 = 8MHz) will be visible on pin PG[7] */
    /* Pin PG[7] is configured in SIUL2_Init() function */
    MC_CGM.AC6_DC0.B.DE = 1;		/* Enable output clock devided */
    MC_CGM.AC6_DC0.B.DIV = 0x13;	/* Divide output clock by 20 */
    MC_CGM.AC6_SC.B.SELCTL = 0x2; 	/* PLL_CLKOUT1 */    
}//System_Clk_160mhz

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

/*******************************************************************************
Function Name : EraseFlash
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : - FlashEraseError
                - noErr
Notes         : Erase whole flash except area occupied by bootloader:
                blocks 0x00F8C000--0x00F8FFFF and 0x00F90000--0x00F93FFF
Issues        : NONE
*******************************************************************************/							
UINT8 EraseFlash(void)
{
    UINT32 opResult = 0;
    UINT32 lowBlockSelect;
    UINT32 midBlockSelect;
    UINT32 highBlockSelect;
    N256K_BLOCK_SEL n256KBlockSelect;

    OutStr("\r\nPlease wait...\r\n");
    
    /* Select all blocks except blocks occupied by bootloader */    
    lowBlockSelect = 0x17C;
    midBlockSelect = 0x3FE;
    highBlockSelect = 0x0;
    n256KBlockSelect.first256KBlockSelect = 0x003FFFFF;
    n256KBlockSelect.second256KBlockSelect = 0x0;
    
    returnCode = pFlashErase(&ssdConfig, 
                             C55_ERASE_MAIN, 
                             lowBlockSelect, 
                             midBlockSelect, 
                             highBlockSelect,
                             n256KBlockSelect);

    if(C55_OK == returnCode)
    {
       /* Wait until the operation finishes */
       while ((returnCode = pFlashCheckStatus(&ssdConfig, C55_MODE_OP_ERASE,&opResult, &CtxData)) == C55_INPROGRESS);
    }
    else
    {
	return(FlashEraseError);
    }
    return(noErr);
}//EraseFlash

/*******************************************************************************
Function Name : ProgramFlash
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : - FlashProgramError
                - SRecOddError
		- BadHexData
		- SRecTooLong
		- CheckSumErr
                - noErr
Notes         : Receive s-record file and program data to flash. 
Issues        : NONE
*******************************************************************************/
UINT8 ProgramFlash(void)
{
    UINT32 opResult = 0;
    UINT32 dest;
    UINT32 size;
    UINT32 source;
    SRecDataRec ProgSRec;
    UINT8 Error = noErr; 

    OutStr("\r\nSend the SRecord file!\r\n");
    
    for(;;)
    {
	/* go get an S-Record, return if there was an error */
	Error = RcvSRecord(&ProgSRec);
	if (Error != 0)
	    break;

	if (ProgSRec.RecType == EndRec)	/* S7, S8 or S9 record? */
	    break;			/* yes. return */

	else if (ProgSRec.RecType == HeaderRec)	/* S0 record? */
	    continue;			/* yes. just ignore it */

	/* a data record was received */  
	else                    
	{      
	    if ((ProgSRec.LoadAddr & 0x0000000FUL) != 0) /* S-Record address aligned? */
		return(SRecOddError);	/* address not aligned. return */
	    if (ProgSRec.NumBytes != 16)/* S-Record constant length 16? */
		return(SRecOddError);	/* length different from 16. Return */                       

	    /* Prepare data for programming */
	    dest = ProgSRec.LoadAddr;
	    source = (UINT32)ProgSRec.Data;
	    size = 16;

	    returnCode = pFlashProgram(&ssdConfig,FALSE, dest, size, source, &CtxData);

	    if(C55_OK == returnCode)
	    {
		/* Wait until the operation finishes */
		while ((returnCode = pFlashCheckStatus(&ssdConfig, C55_MODE_OP_PROGRAM,&opResult, &CtxData)) == C55_INPROGRESS);	
	    }
	    else
	    {
		return(FlashProgramError);
	    }	   
	}
    }
    return Error;
}//ProgramFlash

/*******************************************************************************
Function Name : AppExecute
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : Start the user application. 
Issues        : NONE
*******************************************************************************/
void AppExecute(void)
{
    UINT32 i;
    
    OutStr("\r\nUser Application is going to be executed...\r\n");

    //some delay
    for(i=0; i<1000000; i++)
	asm("nop");
    
    /* Reset the device to execute user application. Make sure that state
       of PA[1] pin is logic 0 (SW3 is not pushed on EVB). */
    MC_ME.MCTL.R = 0x00005AF0;	//Functional RESET & Key 
    MC_ME.MCTL.R = 0x0000A50F;	//Functional RESET & Key
}//AppExecute

/*******************************************************************************
Function Name : FlashInitAndUnlock
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : - FlashInitError
                - noErr
Notes         : Initialization of SSD flash drivers and unlocking of all flash
                blocks except blocks occupied by bootloader.
Issues        : NONE
*******************************************************************************/
UINT8 FlashInitAndUnlock(void)
{
    /* Flash Init */
    returnCode = pFlashInit(&ssdConfig);    
    if (C55_OK != returnCode)
    {
        return(FlashInitError);	
    }

/*  Unlock all flash blocks except two 16KB blocks which are occupied by
    bootloder:
    (0x00F8C000--0x00F8FFFF and 0x00F90000--0x00F93FFF) */
    
    /* unlock low blocks except 0x00F8C000--0x00F8FFFF */
    returnCode = pSetLock(&ssdConfig, C55_BLOCK_LOW, 0xFFFFFE83);
    if (C55_OK != returnCode)
    {
        return(FlashInitError);
    }
    
    /* unlock mid blocks except 0x00F90000--0x00F93FFF */
    returnCode = pSetLock(&ssdConfig, C55_BLOCK_MID, 0xFFFFFC01);
    if (C55_OK != returnCode)
    {
        return(FlashInitError);
    }

    /* unlock 256K blocks */
    returnCode = pSetLock(&ssdConfig, C55_BLOCK_256K_FIRST, 0xFFC00000);
    if (C55_OK != returnCode)
    {
        return(FlashInitError);
    }    

    return(noErr);
}//FlashInitAndUnlock

/*******************************************************************************
Function Name : main
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : NONE
Issues        : NONE
*******************************************************************************/
void main (void)
{       
    UINT8 c;
    
    Sys_Init();
    INTC_Init();    
    LINFlexD_2_Init();

    OutStr("\f\r\n**********************************************\r\n");
    OutStr("\r\nMPC5748G Bootloader v1.0\r\n");
    OutStr("\r\n**********************************************\r\n");
  
    c = FlashInitAndUnlock();
    if (c != 0)       
	OutStr(GetErrorString(c));  /* report an error if there was one */
  
    for(;;)
    {
	/* display menu */
	OutStr("\r\n1.) Erase Flash\r\n");
	OutStr("2.) Program Flash\r\n");    
	OutStr("3.) Execute Application\r\n");
	OutStr("Please choose 1 to 3: ");
	
	/* get choice */
	/* Filter out characters different from '1' or '2' or '3' */
	do
	{
	    c = GetChar();      
	}while((c<'1') || (c>'3'));	
	
	PutChar(c);   /* echo choice */
	OutStr("\r\n");     /* next line */
    
	switch (c) {
    
	    case '1':	/* erase all the Flash (except the bootloader) */
		c = EraseFlash();
		if (c != 0)       
		    OutStr(GetErrorString(c));  /* and report an error if there was one */
		else
		    OutStr("\r\nErased successfully!\r\n");
		break;
		
	    case '2':	/* receive s-record file and program the flash */
		c = ProgramFlash();
		if (c != 0)       
		    OutStr(GetErrorString(c));  /* and report an error if there was one */
		else
		    OutStr("\r\nProgrammed successfully!\r\n");
		break;
		
	    case '3':	/* execute the application */
		AppExecute();     
		break;
		
	    default:
		break;
	}
    }       
}
