/****************************************************************************\
 *           Copyright (C) 2013 Texas Instruments Incorporated.             *
 *                           All Rights Reserved                            *
 *                                                                          *
 * GENERAL DISCLAIMER                                                       *
 * ------------------                                                       *
 * All software and related documentation is provided "AS IS" and without   *
 * warranty or support of any kind and Texas Instruments expressly disclaims*
 * all other warranties, express or implied, including, but not limited to, *
 * the implied warranties of merchantability and fitness for a particular   *
 * purpose.  Under no circumstances shall Texas Instruments be liable for   *
 * any incidental, special or consequential damages that result from the    *
 * use or inability to use the software or related documentation, even if   *
 * Texas Instruments has been advised of the liability.                     *
 ****************************************************************************
This example shows three main features of Timers on KeyStone device:
1. generate one-shot pulse and interrupt
2. generate continual clock and interrupts
3. generate continual square waves and interrupts
4. watch-dog
 ****************************************************************************
 * Created by :                                                             *
 *            Brighton Feng                                                 *
 *            Texas Instruments                                             * 
 *            March 5, 2012                                                 *
 * Updated by Brighton Feng on Feb 8, 2013 for K2                           *
 * Updated by Brighton Feng on Jun 25, 2014, add test case to generate 
                                        square waves with timer + EDMA      *
***************************************************************************/
#include <stdio.h>
#include <csl_bootcfgAux.h>
#include <csl_edma3.h>
#include "CP15.h"
#include "K2_common.h"
#include "K2_Board_Init.h"

/*delay (in millisecond) before the timer generate one-shot pulse*/
#define PULSE_DELAY_MS 			200

/*period (in millisecond) of the clock generated by timer*/
#define CLOCK_PERIOD_MS 		1
/*number of the clocks generated in this test*/
#define NUM_CLOCKS_GENERATED 	500

/*period (in millisecond) of the waveform generated by timer*/
#define WAVE_PERIOD_MS 		1
/*Duty cycle of the waveform (percentage of low period)*/
#define WAVE_LOW_PERCENT 		66
/*number of the waves generated in this test*/
#define NUM_WAVES_GENERATED 	500

/*period (in millisecond) of the watch-dog timer*/
#define WATCH_DOG_PERIOD_MS 	3000

/*since the timer runs at (Main core clock)/6
the conversion between millisecond and timer PRD value is:
PRD = (xxx_MS/1000)*MAIN_CLK_HZ/6
    = xxx_MS*(MAIN_CLK_HZ/1000)/6*/
    
//MMU memory ranges tables
MMU_Memory_Map_Range memory_ranges[]=
{
	{//MSMC RAM
    .uiVirtualAddress   = 0x0C000000,
    .ullPhysicalAddress = 0x0C000000,
    .uiByteCnt          = MSMC_RAM_SIZE_BYTES,
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_X,
    .shareAttr          = MMU_MEM_ATTR_OUTER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	},                     
	{//DDR3A               
    .uiVirtualAddress   = 0x80000000,
    .ullPhysicalAddress = 0x800000000ULL, 	
#if (DDR3A_SIZE_BYTES>0x80000000ULL)
    .uiByteCnt          = 0x80000000, 	
#else
    .uiByteCnt          = DDR3A_SIZE_BYTES, 	
#endif
    .attribute          = MMU_MEM_ATTR_NORMAL_CACHE_RA_WB_WA,
    .accessPermission   = MMU_MEM_ATTR_RW,
    .exectuePermission  = MMU_MEM_ATTR_XN,
    .shareAttr          = MMU_MEM_ATTR_OUTER_SHARE,
    .isGlobal           = MMU_MEM_ATTR_GLOBAL,
    .isSecure           = MMU_MEM_ATTR_SECURE,
	}
};

MMU_Long_Format_Config mmu_cfg=
{
	.memory_map        = memory_ranges,
	.uiNumMemMapRanges = sizeof(memory_ranges)/sizeof(MMU_Memory_Map_Range),
	.ullpMMU3rdLevelTT = NULL, 	//No level 3 translation table
	.bAlignCheck       = TRUE,
	.tableCacheAttr    = MMU_TAB_ATTR_CACHE_WB_WA,
	.tableShareAttr	   = MMU_MEM_ATTR_INNER_SHARE,
};

volatile Uint32 uiTick = 0;

//Interrupt service routine for timer
void Timer_ISR(Uint32 uiIAR_value, Uint32 uiIntAddress)
{
	uiTick++;
}

void Timer_Interrupts_Init(void)
{
	GIC_INT_Config int_cfg;

	int_cfg.trigger_type= GIC_TRIGGER_TYPE_EDGE;
	int_cfg.ucGroupNum= 1; //route timer interrupt to group 1, IRQ
	int_cfg.ucPriority= GIC400_PRIORITY_LOWEST; 
	GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_TIMER_16_INTL+2*CP15_get_CPU_ID()), 
		&int_cfg, Timer_ISR);
	GIC_interrupt_hook(GIC_CONVERT_SPI_ID(CSL_ARM_GIC_TIMER_8_INTL), 
		&int_cfg, Timer_ISR);
}

/*use local timer (==(16+CP15_get_CPU_ID())) to generate a pulse on TIMO0,
a interrupt is generated as well*/
void generate_pulse_by_timer()
{
	Timer64_Config tmrCfg;
	
	puts("\na pulse will be generated on TIMO0...");

	uiTick = 0;

	//select output on TIMO0 from local timer.
#ifdef CSL_BOOTCFG_TOUTPSEL0_TOUTPSEL0_SHIFT
	gpBootCfgRegs->TOUTPSEL0 = ((16+CP15_get_CPU_ID())*2)<<CSL_BOOTCFG_TOUTPSEL0_TOUTPSEL0_SHIFT;
#else
	gpBootCfgRegs->TOUTSEL = ((16+CP15_get_CPU_ID())*2)<<CSL_BOOTCFG_TOUTSEL_TOUTSEL0_SHIFT;
#endif

	//configure the timer for one shot pulse
	tmrCfg.timer_num= (16+CP15_get_CPU_ID());
	tmrCfg.timerMode= TIMER_ONE_SHOT_PULSE;
	tmrCfg.period= (unsigned long long)PULSE_DELAY_MS*gMain_Core_Speed_Hz/6000;
	tmrCfg.reload_period= 0; //not used for this case
	tmrCfg.pulseWidth= 3;
	Timer64_Init(&tmrCfg);

	//wait until the interrupt happen, which means the pulse has been generated
	while(0==uiTick)
	{
		//ARM_poll_interrupts();
		delay_ms(10);
	}

	puts("a pulse was generated on TIMO0\n");
}

/*use local timer (==(16+CP15_get_CPU_ID())) to generate a clock on TIMO0,
interrupts are generated as well*/
void generate_clocks_by_timer()
{
	Timer64_Config tmrCfg;

	puts("clocks will be generated on TIMO0...");

	uiTick = 0;

	//select output on TIMO0 from local timer.
#ifdef CSL_BOOTCFG_TOUTPSEL0_TOUTPSEL0_SHIFT
	gpBootCfgRegs->TOUTPSEL0 = ((16+CP15_get_CPU_ID())*2)<<CSL_BOOTCFG_TOUTPSEL0_TOUTPSEL0_SHIFT;
#else
	gpBootCfgRegs->TOUTSEL = ((16+CP15_get_CPU_ID())*2)<<CSL_BOOTCFG_TOUTSEL_TOUTSEL0_SHIFT;
#endif

	/*configure the timer to generate clocks and interrupts*/
	tmrCfg.timer_num= (16+CP15_get_CPU_ID());
	tmrCfg.timerMode= TIMER_PERIODIC_CLOCK;
	tmrCfg.period= (unsigned long long)CLOCK_PERIOD_MS*gMain_Core_Speed_Hz/6000;
	tmrCfg.reload_period= 0; //not used for this case
	Timer64_Init(&tmrCfg);

	//wait until a number of interupts happen
	while(uiTick<NUM_CLOCKS_GENERATED*2)
	{
		//ARM_poll_interrupts();
		delay_ms(10);
	}
	Reset_Timer((16+CP15_get_CPU_ID())); 	//stop the timer
	
	puts("clocks were generated on TIMO0\n");
}

/*source buffer for EDMA to reload timer registers*/
typedef struct  {
	Uint32 uiLowPeriod; 
	Uint32 uiINT_flag_low; 	//value for clearing interrupt flag
	Uint32 uiHighPeriod; 
	Uint32 uiINT_flag_high; //value for clearing interrupt flag
} TimerReloadBuf;

/*use timer 8 to generate square waves on TIMO1,
interrupts are generated as well.
EDMA is used to reload period register*/
TimerReloadBuf reload_buf;
void generate_waves_by_timer()
{
	Timer64_Config tmrCfg;
	unsigned long long ullPeriod;

	puts("Square waves will be generated on TIMO0...");

	uiTick = 0;

	//select output on TIMO1 from local timer.
#ifdef CSL_BOOTCFG_TOUTPSEL0_TOUTPSEL1_SHIFT
	gpBootCfgRegs->TOUTPSEL0 = (8*2)<<CSL_BOOTCFG_TOUTPSEL0_TOUTPSEL1_SHIFT;
#else
	gpBootCfgRegs->TOUTSEL = (8*2)<<CSL_BOOTCFG_TOUTSEL_TOUTSEL1_SHIFT;
#endif

	ullPeriod= (unsigned long long)WAVE_PERIOD_MS*gMain_Core_Speed_Hz/6000;
	reload_buf.uiLowPeriod= ullPeriod*WAVE_LOW_PERCENT/100;
	reload_buf.uiHighPeriod= ullPeriod - reload_buf.uiLowPeriod;
	reload_buf.uiINT_flag_low= TMR_INTCTLSTAT_EN_ALL_CLR_ALL;
	reload_buf.uiINT_flag_high= TMR_INTCTLSTAT_EN_ALL_CLR_ALL;

	/*setup EDMA to reload low period and high period valud continously,
	the EDMA is triggered by timer event(interrupt)*/
	EDMA_init();
	EDMA_channel_TC_cfg(1, 22, 3); 	//use TC3 for channel 22

	gpEDMA_CC_regs[1]->PARAMSET[22].OPT= 
		CSL_EDMA3_OPT_MAKE(CSL_EDMA3_ITCCH_DIS, 
			CSL_EDMA3_TCCH_DIS, 
			CSL_EDMA3_ITCINT_DIS, 
			CSL_EDMA3_TCINT_EN,
			22,
			CSL_EDMA3_TCC_NORMAL,
			CSL_EDMA3_FIFOWIDTH_NONE, 
			CSL_EDMA3_STATIC_DIS, 
			CSL_EDMA3_SYNC_AB, 
			CSL_EDMA3_ADDRMODE_INCR, 
			CSL_EDMA3_ADDRMODE_INCR);
	gpEDMA_CC_regs[1]->PARAMSET[22].SRC= (Uint32)(&reload_buf);
	gpEDMA_CC_regs[1]->PARAMSET[22].A_B_CNT= CSL_EDMA3_CNT_MAKE(4, 2);
	gpEDMA_CC_regs[1]->PARAMSET[22].DST= (Uint32)&gpTimerRegs[8]->RELLO;
	gpEDMA_CC_regs[1]->PARAMSET[22].SRC_DST_BIDX= CSL_EDMA3_BIDX_MAKE(4, 16);
	gpEDMA_CC_regs[1]->PARAMSET[22].LINK_BCNTRLD= CSL_EDMA3_LINKBCNTRLD_MAKE(64*32, 2);
	gpEDMA_CC_regs[1]->PARAMSET[22].SRC_DST_CIDX= CSL_EDMA3_CIDX_MAKE(8, 0);
	gpEDMA_CC_regs[1]->PARAMSET[22].CCNT= 2;

	/*fill reload PaRAM*/
	gpEDMA_CC_regs[1]->PARAMSET[64].OPT= 
		CSL_EDMA3_OPT_MAKE(CSL_EDMA3_ITCCH_DIS, 
			CSL_EDMA3_TCCH_DIS, 
			CSL_EDMA3_ITCINT_DIS, 
			CSL_EDMA3_TCINT_EN,
			22,
			CSL_EDMA3_TCC_NORMAL,
			CSL_EDMA3_FIFOWIDTH_NONE, 
			CSL_EDMA3_STATIC_DIS, 
			CSL_EDMA3_SYNC_AB, 
			CSL_EDMA3_ADDRMODE_INCR, 
			CSL_EDMA3_ADDRMODE_INCR);
	gpEDMA_CC_regs[1]->PARAMSET[64].SRC= (Uint32)(&reload_buf);
	gpEDMA_CC_regs[1]->PARAMSET[64].A_B_CNT= CSL_EDMA3_CNT_MAKE(4, 2);
	gpEDMA_CC_regs[1]->PARAMSET[64].DST= (Uint32)&gpTimerRegs[8]->RELLO;
	gpEDMA_CC_regs[1]->PARAMSET[64].SRC_DST_BIDX= CSL_EDMA3_BIDX_MAKE(4, 16);
	gpEDMA_CC_regs[1]->PARAMSET[64].LINK_BCNTRLD= CSL_EDMA3_LINKBCNTRLD_MAKE(64*32, 2);
	gpEDMA_CC_regs[1]->PARAMSET[64].SRC_DST_CIDX= CSL_EDMA3_CIDX_MAKE(8, 0);
	gpEDMA_CC_regs[1]->PARAMSET[64].CCNT= 2;

	/*clear event*/
	gpEDMA_CC_regs[1]->TPCC_ECR= 1<<22;
	/*enable event*/
	gpEDMA_CC_regs[1]->TPCC_EESR= 1<<22;
 

	/*configure the timer to generate clocks and interrupts*/
	tmrCfg.timer_num= 8;
	tmrCfg.timerMode= TIMER_PERIODIC_WAVE;
	tmrCfg.period= reload_buf.uiLowPeriod;
	tmrCfg.reload_period= reload_buf.uiHighPeriod;
	Timer64_Init(&tmrCfg);

	//wait until a number of interupts happen
	while(uiTick<NUM_WAVES_GENERATED*2)
	{
		//ARM_poll_interrupts();
		delay_ms(10);
	}
	Reset_Timer(8); 	//stop the timer

	/*disable EDMA event*/
	gpEDMA_CC_regs[1]->TPCC_EECR= 1<<22;
	
	puts("Square waves were generated on TIMO0\n");
}


/*set local timer (==(16+CP15_get_CPU_ID())) as watch dog timer,
service the watch-dog for sometime, 
after stop servicing watch-dog, let it trigger exception*/
void watchdog_timer_test()
{
	int i;
	Uint32 timerCnt, timerNum;
	Timer64_Config tmrCfg;
	volatile Uint32 * RSTMUX;


	timerNum= 16+CP15_get_CPU_ID();
	//select output on TIMO0 from local timer.
#ifdef DEVICE_K2L 	//for K2L
	gpBootCfgRegs->TOUTPSEL0 = (timerNum*2)<<CSL_BOOTCFG_TOUTPSEL0_TOUTPSEL0_SHIFT;
#else 	
	gpBootCfgRegs->TOUTSEL = (timerNum*2)<<CSL_BOOTCFG_TOUTSEL_TOUTSEL0_SHIFT;
#endif

#ifdef DEVICE_K2E
	RSTMUX = &gpBootCfgRegs->RSTMUX8; 
	RSTMUX += CP15_get_CPU_ID();
#else
	RSTMUX = &gpBootCfgRegs->RSTMUX[(8+CP15_get_CPU_ID())]; 
#endif
	*RSTMUX= (7<<CSL_BOOTCFG_RSTMUX0_RSTMUX_DELAY0_SHIFT)
		/*WD timer event trigger GIC interrupt*/
		|(WD_TRIGGER_GIC<<CSL_BOOTCFG_RSTMUX0_RSTMUX_OMODE0_SHIFT);
		
	puts("start watch-dog timer...");

	//configure the timer for watch-dog
	tmrCfg.timer_num= timerNum;
	tmrCfg.timerMode= TIMER_WATCH_DOG;
	tmrCfg.period= (unsigned long long)WATCH_DOG_PERIOD_MS*gMain_Core_Speed_Hz/6000;
	tmrCfg.reload_period= 0; //not used for this case
	tmrCfg.pulseWidth= 3;
	Timer64_Init(&tmrCfg);

	//service the watch-dog for sometime, 
	for(i=1; i<=10; i++)
	{
		timerCnt= gpTimerRegs[timerNum]->CNTLO;
		Service_Watchdog(timerNum);
		printf("service watch-dog %d times, at time counter = %u\n", 
			i, timerCnt);
	}

	printf("!!!stop servicing watch-dog, it will timeout and trigger exception after %d ms...\n",
		WATCH_DOG_PERIOD_MS);

	/*wait for watch dog timer expires*/
	while(gpTimerRegs[timerNum]->CNTLO);
	/*add dummy nop here to wait for the exception happens*/
	dummy_wait(10000);	
}

void timer_calibrate()
{
	int i;
	Timer64_Config tmrCfg;
	Uint32 timer_cycle[8], PMU_cycle[8];
	
	/*configure the timer to generate clocks and interrupts*/
	tmrCfg.timer_num= (16+CP15_get_CPU_ID());
	tmrCfg.timerMode= TIMER_PERIODIC_PULSE;
	tmrCfg.period= 0xFFFFFFFF;
	tmrCfg.reload_period= 0; //not used for this case
	Timer64_Init(&tmrCfg);

	for(i=0; i<8; i++)
	{
		timer_cycle[i]= gpTimerRegs[tmrCfg.timer_num]->CNTLO;
		PMU_cycle[i]= CP15_read_CCNT();
		delay_ms(100);
	}

	for(i=0; i<8; i++)
	{
		printf("timer cycle= %9u , PMU cycle = %9u\n", 
			6*timer_cycle[i],	PMU_cycle[i]);
	}

	while(1);
}

int main()
{
	/*common initialization for internal modules in K2 device 
	enable GIC, memory protection interrupts, EDC for MSMC RAM */
	K2_common_device_init();
	/*initialize GIC interface for CPU, enable IRQ, FIQ, PMU*/
	K2_common_CPU_init();
	KeyStone_Exception_cfg(TRUE);

	EDMA_init();
	K2_SMS_MPAX_init(PRIVID_ARM_COREPAC, MSMC_SHARE);

	//Main core speed= MAIN_PLL_REF_CLK_MHZ*MAIN_PLL_MULTIPLIER/MAIN_PLL_DIVISOR
	KeyStone_main_PLL_init(MAIN_PLL_REF_CLK_MHZ, MAIN_PLL_MULTIPLIER, MAIN_PLL_DIVISOR);

#ifndef DEVICE_K2E 	//K2E only has main PLL for both ARM and DSP cores
	//ARM core speed= ARM_PLL_REF_CLK_MHZ*ARM_PLL_MULTIPLIER/ARM_PLL_DIVISOR
	K2_ARM_PLL_init(ARM_PLL_REF_CLK_MHZ, ARM_PLL_MULTIPLIER, ARM_PLL_DIVISOR);
	//K2_ARM_PLL_init(125, 8, 1);
#endif

	/*MMU configure for ARM core*/
	MMU_long_format_init(&mmu_cfg);
	CP15_ICacheEnable();
	CP15_DCacheEnable();

	//timer_calibrate();
	//while(1);

	Timer_Interrupts_Init();

	generate_pulse_by_timer();
	generate_clocks_by_timer();
	generate_waves_by_timer();
	watchdog_timer_test();

	return 0;
}

