/*******************************************************************************
* Freescale Semiconductor Inc.
* (c) Copyright 2011 Freescale Semiconductor, Inc.
* ALL RIGHTS RESERVED.
********************************************************************************
Services performed by FREESCALE 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. FREESCALE neither guarantees nor will be
held liable by CUSTOMER for the success of this project.
FREESCALE 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 FREESCALE, AND OR NAY PRODUCT RESULTING FROM FREESCALE 
SERVICES. IN NO EVENT SHALL FREESCALE BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL 
DAMAGES ARISING OUT OF THIS AGREEMENT.
CUSTOMER agrees to hold FREESCALE 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:            b05111
* Version:          0.0
* Date:             Jul-31-2014
* Classification:   General Business Information
* Brief:            pin toggle stationery for MPC5643L
********************************************************************************
* Detailed Description: 
* Example demostrates MCU behaviour when single bit RAM ECC error occurs by 
* intentional ECC error injection.
*
* ------------------------------------------------------------------------------
* Test HW:         xPC564xLKIT, PPC5643L Cut3 silicon
* Target :         internal_FLASH
* Fsys:            120 MHz PLL0
* Debugger:        Lauterbach Trace32
*                  PeMicro USB-ML-PPCNEXUS
* Terminal:        19200-8-no parity-1 stop bit-no flow control via LINFlex0
* EVB connection:  default 
*
********************************************************************************
Revision History:
0.0     Jul-31-2014  b05111  Initial Version
                    
*******************************************************************************/
#include <stdio.h>
#include "MPC5643L.h"
#include "uart.h"
#include "IntcInterrupts.h"
#include "Exceptions.h"

/*******************************************************************************
* External objects
*******************************************************************************/

/*******************************************************************************
* Global variables
*******************************************************************************/

vint32_t ecc_1b_handler_pass_count = 0;


/*******************************************************************************
* Constants and macros
*******************************************************************************/

/* FCCU keys */
#define FCCU_NCFK_KEY 0xAB3498FE
#define FCCU_CFK_KEY  0x618B7A50

/* PCR numbers */
#define PD4_pin 52
#define PD5_pin 53  
#define PD6_pin 54
#define PB6_pin 22

#define LED1_pin   PD4_pin
#define LED2_pin   PD5_pin
#define LED3_pin   PD6_pin
#define CLKOUT_pin PB6_pin

/* ECSM */
#define ECSM_ECR_EFNCR_MASK     0x01
#define ECSM_ECR_ERNCR_MASK     0x02
#define ECSM_ECR_EF1BR_MASK		0x10
#define ECSM_ECR_ER1BR_MASK     0x20

// Force RAM Continuous Noncorrectable Data Inversions
#define ECSM_EEGR_FRCNCI_MASK   0x0200
// Force RAM One Noncorrectable Data Inversions
#define ECSM_EEGR_FR1NCI_MASK   0x0100
// Force RAM Continuous 1-Bit Data Inversions
#define ECSM_EEGR_FRC1BI_MASK   0x2000
// Force RAM One 1-bit Data Inversion
#define ECSM_EEGR_FR11BI_MASK   0x1000

// remame ECSM module to MCM (Miscellaneous Control Module)
#define ECSM SPP_MCM


/*******************************************************************************
* Local types
*******************************************************************************/

/*******************************************************************************
* Local function prototypes
*******************************************************************************/
static void HW_init(void);
static void ECSM_init(void);
static void SysClk_init(void);
static void ModesAndClks_init(void);
static void PeriClkGen_init(void);
static void DisableWatchdog(void);
static void GPIO_init(void);
static void ClearNCF(void);
static void ClearCF(void);
static void ClearFails(void);

static void ECSM_1bit_RAM_err_handler(void);
static void Generate_1bit_RAM_ECC_error(void);
static void Fix_1bit_error_RAM_data(void);


/*******************************************************************************
* Local variables
*******************************************************************************/
static vuint8_t  ecsm_esr = 0;
static vuint32_t ecsm_rear = 0;
static vuint8_t  ecsm_remr = 0;
static vuint8_t  ecsm_reat = 0;

/* align this to a 0-modulo-8 address (aligned to 8 bytes) */
static vuint32_t test[2] __attribute__ ((aligned (8))) = 
{
    0xBABADEDA,
    0xCAFEBEEF
};

static vuint32_t test_read = 0;


/*******************************************************************************
Function Name : HW_init
Engineer      : b08110
Date          : Feb-18-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : initialization of the hw for the purposes of this example
Issues        : 
*******************************************************************************/
static void HW_init(void)
{
    DisableWatchdog();
    ClearFails();
    SysClk_init();    
    GPIO_init();    
}


/*******************************************************************************
Function Name : SysClk_init
Engineer      : b08110
Date          : Feb-25-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void SysClk_init(void)
{
    ModesAndClks_init();
    PeriClkGen_init();
}

/*******************************************************************************
Function Name : ModesAndClks_init
Engineer      : b08110
Date          : Aug-26-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : - DRUN->RUN1->RUN0->RUN1
                - enable CLKOUT
Issues        : 
*******************************************************************************/
static void ModesAndClks_init(void) 
{
    int32_t cnt = 0;
    
    /* Enable DRUN, RUN0, RUN1 SAFE, RESET modes */
    ME.MER.R = 0x0000003D;
    
    /* Mode Transition to enter RUN1 mode: */
        
    // RUN1 cfg: 16MHzIRCON,OSC0ON,PLL0OFF,PLL1OFF,syclk=16MIRC
    ME.RUN[1].R = 0x001F0030;
    // Enter RUN1 Mode & Key
    ME.MCTL.R = 0x50005AF0;
    // Enter RUN1 Mode & Inverted Key
    ME.MCTL.R = 0x5000A50F;
    // Wait for mode entry to complete
    while(0 == ME.GS.B.S_XOSC) {};
    // Wait for mode transition to complete
    while(1 == ME.GS.B.S_MTRANS) {}
    // Check RUN1 mode has been entered
    while(5 != ME.GS.B.S_CURRENT_MODE) {};
    
    
    /* Initialize PLL before turning it on:
       fsys = fcrystal*ndiv/idf/odf
       fvco must be from 256MHz to 512MHz
       we want fsys = 120MHz. fvco = fsys*odf = 120MHz * 4 = 480MHz
       fsys = 40*72/6/4 = 120MHz */

    CGM.AC3SC.R = 0x01000000; // Select Xosc as PLL0 source clock
    CGM.AC4SC.R = 0x01000000; // Select Xosc as PLL1 source clock

#if 0
    /* 80 MHz */
    CGM.FMPLL[0].CR.B.IDF = 0x7; // FMPLL0 IDF=5 --> divide by 8
    CGM.FMPLL[0].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[0].CR.B.NDIV = 64; // FMPLL0 NDIV=72 --> divide by 64
    
    /* 120 MHz */
    CGM.FMPLL[1].CR.B.IDF = 0x5; // FMPLL0 IDF=5 --> divide by 8
    CGM.FMPLL[1].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[1].CR.B.NDIV = 72; // FMPLL0 NDIV=72 --> divide by 64
#else
    /* 120 MHz */
    CGM.FMPLL[0].CR.B.IDF = 0x5; // FMPLL0 IDF=5 --> divide by 6
    CGM.FMPLL[0].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[0].CR.B.NDIV = 72; // FMPLL0 NDIV=72 --> divide by 72
    
    /* 80 MHz */
    CGM.FMPLL[1].CR.B.IDF = 0x7; // FMPLL0 IDF=5 --> divide by 8
    CGM.FMPLL[1].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[1].CR.B.NDIV = 64; // FMPLL0 NDIV=72 --> divide by 64
#endif

    CGM.FMPLL[0].CR.B.EN_PLL_SW = 1; // enable progressive clock switching
    CGM.FMPLL[1].CR.B.EN_PLL_SW = 1; // enable progressive clock switching
    
    ME.RUNPC[0].R = 0x000000FE; // enable peripherals run in all modes
    ME.LPPC[0].R = 0x00000000;  // disable peripherals run in LP modes

#if 0     
    /* Mode Transition to enter RUN0 mode: */
    
    // RUN0 cfg: 16MHzIRCON,OSC0ON,syclk=OSC
    ME.RUN[0].R = 0x001F0032;       
    // Enter RUN0 Mode & Key
    ME.MCTL.R = 0x40005AF0;         
    // Enter RUN0 Mode & Inverted Key
    ME.MCTL.R = 0x4000A50F;         
    // Wait for mode transition to complete 
    while (ME.IS.B.I_MTC != 1) {}     
    // Clear Transition flag  
    ME.IS.R = 0x00000001;           
#endif
    
    /* Mode Transition to enter RUN0 mode: */
    
    // RUN0 cfg: 16MHzIRCON,OSC0ON,PLL0ON,PLL1ON,syclk=16M IRC
    ME.RUN[0].R = 0x001F00F0;
    // Enter RUN0 Mode & Key
    ME.MCTL.R = 0x40005AF0;
    // Enter RUN0 Mode & Inverted Key
    ME.MCTL.R = 0x4000A50F;
    // Wait for mode transition to complete
    while (1 == ME.GS.B.S_MTRANS) {}
    // Check RUN0 mode has been entered
    while(4 != ME.GS.B.S_CURRENT_MODE) {};
    
    /* Mode Transition to enter RUN1 mode: */
    
    // RUN1 cfg: 16MHzIRCON,OSC0ON,PLL0ON,PLL1ON,syclk=PLL0
    ME.RUN[1].R = 0x001F00F4;
    // Enter RUN1 Mode & Key
    ME.MCTL.R = 0x50005AF0;
    // Enter RUN1 Mode & Inverted Key
    ME.MCTL.R = 0x5000A50F;
    // Wait for mode transition to complete
    while(1 == ME.GS.B.S_MTRANS) {}
    // Check RUN1 mode has been entered
    while(5 != ME.GS.B.S_CURRENT_MODE) {};
    
    /* enable CLKOUT on PB6 */
    SIU.PCR[CLKOUT_pin].R = 0x0600; // ALT1 - PCR[22] - PA = 0b01
    
    /* set CLKOUT divider of 8 */
    CGM.OCDS_SC.R = 0x32000000; // div by 8, system FMPLL, cut2
    CGM.OCEN.B.EN = 1; 			// enable CLKOUT signal
}

/*******************************************************************************
Function Name : PeriClkGen_init
Engineer      : b08110
Date          : Apr-14-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : - Enable auxiliary clock 0-3. from PLL0
Issues        : 
*******************************************************************************/
static void PeriClkGen_init(void)
{
    CGM.AC0SC.R = 0x04000000;  /* MPC56xxP: Select PLL0 for aux clk 0  */
    CGM.AC0DC.R = 0x80800000;  /* MPC56xxP: Enable aux clk 0 div by 1 */
    CGM.AC1SC.R = 0x04000000;  /* MPC56xxP: Select PLL0 for aux clk 1  */
    CGM.AC1DC.R = 0x80000000;  /* MPC56xxP: Enable aux clk 1 div by 1 */
    CGM.AC2SC.R = 0x04000000;  /* MPC56xxP: Select PLL0 for aux clk 2  */
    CGM.AC2DC.R = 0x80000000;  /* MPC56xxP: Enable aux clk 2 div by 1 */
}

/*******************************************************************************
Function Name : DisableWatchdog
Engineer      : b08110
Date          : Jun-23-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : - disable SWT functionality
Issues        : 
*******************************************************************************/
static void DisableWatchdog(void)
{
    SWT.SR.R = 0x0000c520;     /* Write keys to clear soft lock bit */
    SWT.SR.R = 0x0000d928; 
    SWT.CR.R = 0x8000010A;     /* Clear watchdog enable (WEN) */
    
    /* e200 Core Watchdog Timer */
    asm
    {
        li  r3, 0
        mtspr   340, r3
    }
}

/*******************************************************************************
Function Name : GPIO_init
Engineer      : b08110
Date          : Feb-08-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void GPIO_init(void)
{    	 	
 	SIU.GPDO[LED1_pin].R = 1;
    SIU.GPDO[LED2_pin].R = 1;
    SIU.GPDO[LED3_pin].R = 1;
    
    SIU.PCR[LED1_pin].B.PA = 0x00; 
    SIU.PCR[LED2_pin].B.PA = 0x00;
    SIU.PCR[LED3_pin].B.PA = 0x00;
    
    SIU.PCR[LED1_pin].B.OBE = 0x01;
    SIU.PCR[LED2_pin].B.OBE = 0x01;
    SIU.PCR[LED3_pin].B.OBE = 0x01;    
    
}

/*******************************************************************************
Function Name : ClearNCF
Engineer      : b08110
Date          : Apr-08-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void ClearNCF(void)
{
    uint32_t i,b[4];
    for(i=0;i<4;i++)
    {
        FCCU.NCFK.R = FCCU_NCFK_KEY;
        FCCU.NCF_S[i].R = 0xFFFFFFFF;
        while(FCCU.CTRL.B.OPS != 0x3)
        {
        
        };              /* wait for the completion of the operation */
        b[i]=FCCU.NCF_S[i].R;
    }
}

/*******************************************************************************
Function Name : ClearCF
Engineer      : b08110
Date          : Apr-08-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void ClearCF(void)
{
    uint32_t i,a[4];
    for(i=0;i<4;i++)
    {
        FCCU.CFK.R = FCCU_CFK_KEY;
   
        FCCU.CF_S[i].R = 0xFFFFFFFF;
  
        while(FCCU.CTRL.B.OPS != 0x3)
        {
            
        };      /* wait for the completion of the operation */

        a[i]=FCCU.CF_S[i].R;
    }
}

/*******************************************************************************
Function Name : ClearFails
Engineer      : b08110
Date          : Oct-26-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void ClearFails(void)
{
    uint16_t reset_register;
    
    if(RGM.FES.B.F_FCCU_SAFE /*|| RGM.FES.B.F_FCCU_HARD*/)
    {
        reset_register = RGM.FES.R;
        ME.IMTS.R = 0x00000001;
        ClearCF();
        ClearNCF();
        
        RGM.FES.R = 0xFFFF;
        RGM.DES.R = 0xFFFF;
        
        /* re-enter DRUN */
        ME.MCTL.R = 0x30005AF0; /* Enter DRUN Mode & Key */        
        ME.MCTL.R = 0x3000A50F; /* Enter DRUN Mode & Inverted Key */
    }
}


/*******************************************************************************
Function Name : ECSM_init
Engineer      : b05111
Date          : Jul-31-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : initialization of the ECSM module
Issues        : 
*******************************************************************************/
static void ECSM_init(void)
{
    /* enable RAM ECC error reporting */
    ECSM.ECR.R = (uint8_t)ECSM_ECR_ER1BR_MASK ; // non-correctable RAM ECC error
                          
}


/*******************************************************************************
Function Name : ECSM_1bit_RAM_err_handler
Engineer      : b05111
Date          : Jul-31-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : Handles 1-bit ECC RAM error according recommended sequence                            
Issues        : 
*******************************************************************************/
static void ECSM_1bit_RAM_err_handler(void)
{
    for (;;)
    {
        /* 1. Read the ECSM_ESR and save it */
        ecsm_esr = (uint8_t)ECSM.ESR.R;
        
        /* 2. Read and save all the address and attribute reporting registers */
        ecsm_rear = (uint32_t)ECSM.REAR.R;
        ecsm_remr = (uint8_t)ECSM.REMR.R;
        ecsm_reat = (uint8_t)ECSM.REAT.R;
       	 
        /* 3. Re-read the ECSM_ESR and verify the current contents matches 
              the original contents. If the two values are different, go back 
              to step 1 and repeat */
        if (ecsm_esr == (uint8_t)ECSM.ESR.R)
        {
            break;
        }
    }
        
    /* This may not be necessary as 1-bit error is corrected during every read 
       of data word with 1-bit ECC corruption, however read-write sequence 
       physically corrects affected data field */
    Fix_1bit_error_RAM_data();    
    
    /* 4. When the values are identical, write a 1 to the asserted ECSM_ESR flag 
          to negate the interrupt request - we actually negate both flags */
    ECSM.ESR.R = 0x20; 
    
    ecc_1b_handler_pass_count++;   

}


/*******************************************************************************
Function Name : Generate_1bit_RAM_ECC_error
Engineer      : b05111
Date          : Jul-31-2014
Parameters    : 
Modifies      : test 
Returns       : 
Notes         : generates 1-bit ECC error in RAM using the ECSM_EEGR register
Issues        : ECC is being calculated above 64-bit word. RAM[0] of the odd 
                bank means bit 0, RAM[0] of the even bank means bit 32
*******************************************************************************/
static void Generate_1bit_RAM_ECC_error(void)
{
    #if 0
    /* Force internal SRAM one 1-bit correctable data error */
    ECSM.EEGR.R = (uint16_t)(ECSM_EEGR_FR11BI_MASK | 32);
    test[0] = 0xCAFEBEEF; 
    test_read = test[0];  // error caused by read
    test_read = test[1];
  
    #else
    /* Force internal SRAM one 1-bit correctable data error */
    ECSM.EEGR.R = (uint16_t)(ECSM_EEGR_FR11BI_MASK | 0);
    test[1] = 0xCAFEBEEF; 
    test_read = test[1];  // error caused by read
    test_read = test[0];
    #endif
    
}


/*******************************************************************************
Function Name : Fix_1bit_error_RAM_data
Engineer      : b05111
Date          : May-19-2011
Parameters    : 
Modifies      : test
Returns       : 
Notes         : read and write back to physically correct 1-bit error
Issues        : 
*******************************************************************************/
static void Fix_1bit_error_RAM_data(void)
{
    static vuint32_t test_temporary[2];
    
    test_temporary[0] = test[0];
    test_temporary[1] = test[1];
    test[0] = test_temporary[0];        
    test[1] = test_temporary[1]; 
}


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

int32_t main(void) 
{
  
    HW_init(); 
    
    /* install ECSM combined handler to the vector table */
    INTC_InstallINTCInterruptHandler(ECSM_1bit_RAM_err_handler,36,5);  
    
    /* lower current INTC priority to start INTC interrupts */
    INTC.CPR_PRC0.R = 0;
    
    ECSM_init();
    
    Generate_1bit_RAM_ECC_error();
    
    
    if (ecc_1b_handler_pass_count == 1)
    {
        printf("1bit RAM ECC error has been injected and physically corrected\r\n");
    }            
      
    /* Loop forever */
    for (;;) 
    {
        asm (nop);
    }

}



