/** @file system.c 
*   @brief System Driver Source File
*   @date 15.March.2013
*   @version 03.05.00
*
*   This file contains:
*   - API Functions
*   .
*   which are relevant for the System driver.
*/

/* (c) Texas Instruments 2009-2013, All rights reserved. */

/* USER CODE BEGIN (0) */
/* USER CODE END */


/* Include Files */

#include "system.h"
#include "reg_flash.h"

/* USER CODE BEGIN (1) */
/* USER CODE END */

/** @fn void systemInit(void)
*   @brief Initializes System Driver
*
*   This function initializes the System driver.
*
*/
void systemInit(void)
{
/* USER CODE BEGIN (2) */
/* USER CODE END */

    /** @b Initialize @b Flash @b Wrapper: */

    /** - Setup flash read mode, address wait states and data wait states */
    flashWREG->F035_FRDCNTL =  0x01000000U
                       | (2U << 8U)     //2 wait states is need for 80MHz HCLK
                       | (0U << 4U) 
                       |  1U;           //enable pipeline

    /** - Setup flash bank power modes */
    flashWREG->F035_FBFALLBACK = 0x05050000
                          | (SYS_SLEEP << 14U) 
                          | (SYS_SLEEP << 12U) 
                          | (SYS_SLEEP << 10U) 
                          | (SYS_SLEEP << 8U) 
                          | (SYS_SLEEP << 6U) 
                          | (SYS_SLEEP << 4U) 
                          | (SYS_ACTIVE << 2U) 
                          |  SYS_ACTIVE;

    /* Disable Flash ECC */

//    flashWREG-> F035_FEDACCTRL1 &= 0xFFFFFFF0;
//    flashWREG-> F035_FEDACCTRL1 |= 0x05;


    /** @b Initialize @b Pll: */

    /** - Setup pll control register 1:
    *     - Setup reset on oscillator slip 
    *     - Setup bypass on pll slip
    *     - Setup Pll output clock divider
    *     - Setup reset on oscillator fail
    *     - Setup reference clock divider 
    *     - Setup Pll multiplier          
    */
    systemREG->PLLCTL1  =  0x00000000U 
                        |  0x20000000U 
                        | (1U - 1U << 24U) 
                        |  0x00000000U 
                        | (4U - 1U << 16U) 
                        | (100U  - 1U << 8U);

    /** - Setup pll control register 1 
    *     - Enable/Disable frequency modulation
    *     - Setup spreading rate
    *     - Setup bandwidth adjustment
    *     - Setup internal Pll output divider
    *     - Setup spreading amount
    */
    systemREG->PLLCTL2  = 0x00000000U
                        | (255U << 22U)
                        | (0U << 12U)
                        | (5U - 1U << 9U)
                        |  61U;


    /** @b Initialize @b Clock @b Tree: */

    /** - Start clock source lock */
    systemREG->CSDIS =	0x00000000U 
						| 0x00000000U 
						| 0x00000008U 	
						| 0x00000004U 
						| 0x00000000U;

    /** - Wait for until clocks are locked */
    while ((systemREG->CSVSTAT & ((systemREG->CSDIS ^ 0xFF) & 0xFF)) != ((systemREG->CSDIS ^ 0xFF) & 0xFF));

    /** - Setup GCLK, HCLK and VCLK clock source for normal operation, power down mode and after wakeup */
    systemREG->GHVSRC = (SYS_PLL << 24U) 
                       | (SYS_PLL << 16U) 
                       |  SYS_PLL;

    /** - Power-up all peripherals */
    pcrREG->PSPWRDWNCLR0 = 0xFFFFFFFFU;
    pcrREG->PSPWRDWNCLR1 = 0xFFFFFFFFU;
    pcrREG->PSPWRDWNCLR2 = 0xFFFFFFFFU;
    pcrREG->PSPWRDWNCLR3 = 0xFFFFFFFFU;

    /** - Setup synchronous peripheral clock dividers for VCLK1 and VCLK2 */
    systemREG->CLKCNTL  = (systemREG->CLKCNTL & 0xFFF0FFFFU) 
                         | (15U << 16U); 

    systemREG->CLKCNTL  = (systemREG->CLKCNTL & 0xF0F0FFFFU) 
						 | ((1U -1U) << 24U)
	                     | ((1U - 1U) << 16U);  

    /** - Setup RTICLK1 clock */
    systemREG->RCLKSRC  = (1U << 8U)  
                        |  SYS_VCLK;

    /** - Setup asynchronous peripheral clock sources for AVCLK1 and AVCLK2 */
    systemREG->VCLKASRC  = SYS_VCLK;

    /** - Enable Peripherals */
    systemREG->CLKCNTL |= 1U << 8U;

    /** Configure ECLK as cock output. Normally CLK is 80MHz for my application */
    systemREG->SYSPC1 = 0x00000001U;
    systemREG->ECPCNTL |= (0x01 << 23);  /* configure ECLK to use VCLK */
    systemREG->CLKTEST |= (0x5 << 16);  /* Clock going to ECLK pin is enabled */

    /** @note: HCLK >= VCLK2 >= VCLK_sys */

/* USER CODE BEGIN (3) */
/* USER CODE END */
}

/** @fn void _memoryInit()
*   @brief Memory Initialization Driver
*
*   This function is called to perform Memory initialization of selected RAM's.
*/
void _memoryInit()
{
/* USER CODE BEGIN (4) */
/* USER CODE END */

	/* Enable Memory Hardware Initialization */
	systemREG->MINITGCR = 0xAU;	
	
    /* Enable Memory Hardware Initialization for selected RAM's */
	systemREG->MSINENA  = 0x0039;
    
	/* Wait until Memory Hardware Initialization complete */
	while((systemREG->MSTCGSTAT & 0x00000100U) != 0x00000100U)
	{ 
	}/* Wait */ 		
	
	/* Disable Memory Hardware Initialization */
	systemREG->MINITGCR = 0x5U;	
	
/* USER CODE BEGIN (5) */
/* USER CODE END */
}

void systemPowerDown(uint32 mode)
{
    /* Disable clock sources */
    systemREG->CSDISSET = mode & 0x000000FFU;

    /* Disable clock domains */
    systemREG->CDDIS = (mode >> 8U) & 0x000000FFU;

    /* Idle CPU */
    asm(" wfi");
}

/* USER CODE BEGIN (6) */
/* USER CODE END */

void _mpuInit(void)
{
    /* Setting Region 0 */
    M3CORE->MPURNR = REGION0;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Setting Region 1 */
    M3CORE->MPURNR = REGION1;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Setting Region 2 */
    M3CORE->MPURNR = REGION2;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Setting Region 3 */
    M3CORE->MPURNR = REGION3;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Setting Region 4 */
    M3CORE->MPURNR = REGION4;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Setting Region 5 */
    M3CORE->MPURNR = REGION5;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Setting Region 6 */
    M3CORE->MPURNR = REGION6;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Setting Region 7 */
    M3CORE->MPURNR = REGION7;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = 0x08000000U;
    /* load MPU region attribute register, MPU region size, enable/Disable the region */
    M3CORE->MPURASR0 = AP_EXE_NOACCESS | Size_32B | 0U;

    /* Enable MPU */ 
    M3CORE->MPUCR = 0;
}

void mpuEnable(void)
{
    /* Enable MPU */
    M3CORE->MPUCR = 1U;
}

void mpuDisable(void)
{
    /* Enable MPU */ 
    M3CORE->MPUCR = 0U;
}

void mpuSetRegion(uint32 region, uint32 base_addr, uint32 size, uint32 access)
{
    /* Setting Region */
    M3CORE->MPURNR = region;
    /* Setting the base address for region */
    M3CORE->MPURBAR0 = base_addr;
    /* load MPU region attribute register, MPU region size, enable the region */
    M3CORE->MPURASR0 = access | size | 0x1U; 
}
void mpuDisableRegion(uint32 region)
{
    /* Setting Region */
    M3CORE->MPURNR = region;
    /* Disable the region */
    M3CORE->MPURASR0 = 0U; 
}

void _switch_to_usermode()
{
   	
	asm("  subs sp,sp,#4");
	asm("  str r0,[sp]");

	asm(" mov  r0,#0x01");
	asm(" msr  CONTROL,r0");

	asm(" ldr r0,[sp]");
	asm(" add sp,#4");
}	 

/* USER CODE BEGIN (7) */
/* USER CODE END */
