/*
 *  ======== main.c ========
 */

#include <xdc/std.h>

#include <xdc/runtime/System.h>
#include <xdc/runtime/Timestamp.h>

#include <ti/sysbios/BIOS.h>

#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/hal/Cache.h>

#include <ti/csl/csl_chip.h>
#include <ti/csl/csl_psc.h>
#include <ti/csl/csl_pscAux.h>
#include <ti/csl/cslr_bootcfg.h>
#include <ti/csl/cslr_upp.h>

/*
 *  ======== taskFxn ========
 */

#define REG_UPP_BASE		0x02580000
#define REG_DEVCTRL_BASE	0x02620000

#define PACKET_LENGTH		64
#define CLOCK_DIVISOR		4 // upto 15

#pragma DATA_SECTION (g_txBuffer, ".upp_buffer");
#pragma DATA_ALIGN (g_txBuffer, 64);
Int g_txBuffer[PACKET_LENGTH];

#pragma DATA_SECTION (g_rxBuffer, ".upp_buffer");
#pragma DATA_ALIGN (g_rxBuffer, 64);
Int g_rxBuffer[PACKET_LENGTH];

static UInt l2_global_address (UInt addr)
{
	UInt corenum;

	/* Get the core number. */
	corenum = CSL_chipReadReg(CSL_CHIP_DNUM);

	/* Compute the global address. */
	return (addr + (0x10000000 + (corenum*0x1000000)));
}

static Void bootcfg_reg_unlock (Void)
{
    *(volatile UInt *)0x02620038 = 0x83e70b13;
    *(volatile UInt *)0x0262003C = 0x95a4f1e0;
}

static Void bootcfg_reg_lock (Void)
{
    *(volatile UInt *)0x02620038 = 0;
    *(volatile UInt *)0x0262003C = 0;
}

Void taskFxn(UArg a0, UArg a1)
{
	Int i;
	UInt time0, time1, diff, calibration;

    CSL_UppRegs *uppRegs = (CSL_UppRegs *)REG_UPP_BASE;

#if 0
    CSL_BootcfgRegs *bootRegs = (CSL_BootcfgRegs *)REG_DEVCTRL_BASE;
#endif

    // Get calibration value for timestamp
    time0 = Timestamp_get32 ();
    time1 = Timestamp_get32 ();
    if (time1 > time0) {
    	calibration = time1 - time0;
    }
    else {
    	calibration = time0 - time1;
    }

    // Initialize rx/tx buffer
    for (i = 0; i < PACKET_LENGTH; i++) {
    	g_txBuffer[i] = i;
    	g_rxBuffer[i] = 0xFFFFFFFF;
    }

    // Register configurations

    // 1. Apply the appropriate pin multiplexing settings. See the device-specific data manual, and/or
    //pin multiplexing utility for more information.

    bootcfg_reg_unlock ();
#if 0
    bootRegs->CHIP_PIN_CONTROL_1 = CSL_BOOTCFG_CHIP_PIN_CONTROL_1_UPP_EMIF16_MASK;
#else
    *(volatile UInt *)0x02620584 = 0x00000001; // PIN_CONTROL_1 regisger config -- use UPP
    *(volatile UInt *)0x0262016C = 0x00000000; // UPP_CONTROL register config -- use SYSCLK4 (CPU/3)
#endif
    bootcfg_reg_lock ();

    // 2. Enable the power and clocks to the uPP peripheral. See the device-specific data manual for more
    //information.

    //...Nothing...

    // 3. Set the SWRST bit in the uPP peripheral control register (UPPCR) to 1 to place uPP in software
    //reset.
    uppRegs->UPPCR =
    		(CSL_UPP_UPPCR_SWRST_RESET << CSL_UPP_UPPCR_SWRST_SHIFT) |
    		(CSL_UPP_UPPCR_FREE_ENABLE << CSL_UPP_UPPCR_FREE_SHIFT);

    // 4. Wait at least 200 device clock cycles, then clear the SWRST bit to 0 to bring the module out of
    //reset.
    for (i = 0; i < 200; i++) {
    	asm (" nop");
    }
    uppRegs->UPPCR &= ~CSL_UPP_UPPCR_SWRST_MASK;

    // 5. Program the uPP configuration registers: UPCTL, UPICR, UPIVR, UPTCR, and UPDLB.
    uppRegs->UPCTL =
    		(CSL_UPP_UPCTL_MODE_DUPLEX0 << CSL_UPP_UPCTL_MODE_SHIFT) |	//Using Duplex mode 0
			(CSL_UPP_UPCTL_CHN_TWO << CSL_UPP_UPCTL_CHN_SHIFT) |		//2 channels - A and B
			(CSL_UPP_UPCTL_SDRTXIL_DISABLE << CSL_UPP_UPCTL_SDRTXIL_SHIFT) |
			(CSL_UPP_UPCTL_DPWA_FULL << CSL_UPP_UPCTL_DPWA_SHIFT) |
			(CSL_UPP_UPCTL_DPWB_FULL << CSL_UPP_UPCTL_DPWB_SHIFT) |
			(CSL_UPP_UPCTL_IWA_8BIT << CSL_UPP_UPCTL_IWA_SHIFT) |
			(CSL_UPP_UPCTL_IWB_8BIT << CSL_UPP_UPCTL_IWB_SHIFT) |
			(CSL_UPP_UPCTL_DRA_SINGLE << CSL_UPP_UPCTL_DRA_SHIFT) |
			(CSL_UPP_UPCTL_DRB_SINGLE << CSL_UPP_UPCTL_DRB_SHIFT) |
			(CSL_UPP_UPCTL_DDRDEMUX_DISABLE << CSL_UPP_UPCTL_DDRDEMUX_SHIFT) |
			(CSL_UPP_UPCTL_DPFA_RJZE << CSL_UPP_UPCTL_DPFA_SHIFT) |
			(CSL_UPP_UPCTL_DPFB_RJZE << CSL_UPP_UPCTL_DPFB_SHIFT)
			;

    uppRegs->UPICR =
    		//Transmit -- B channel
    		//START is an output signal and is always driven.
    		//ENABLE is an output signal and is always driven.
    		//WAIT is an input signal and may be disabled using the WAITx bit in UPICR.
    		//CLOCK is an output signal.
    		(CLOCK_DIVISOR << CSL_UPP_UPICR_CLKDIVB_SHIFT) |
    		(CSL_UPP_UPICR_WAITB_ENABLE << CSL_UPP_UPICR_WAITB_SHIFT) | //Using wait signal with default polality
    		(CSL_UPP_UPICR_STARTB_ENABLE << CSL_UPP_UPICR_STARTB_SHIFT) | //Using START signal
    		(CSL_UPP_UPICR_ENAB_ENABLE << CSL_UPP_UPICR_ENAB_SHIFT) | //Using ENA signal

    		//Receive -- A channel
    		//START is an input signal and may be disabled using the STARTx bit in UPICR.
    		//ENABLE is an input signal and may be disabled using the ENAx bit in UPICR.
    		//WAIT is an output signal.
    		//CLOCK is an input signal.
    		(CLOCK_DIVISOR << CSL_UPP_UPICR_CLKDIVA_SHIFT) |//biospsp_01_30_01's driver implementation is configuring clock divisor for both Channel A and B
    		(CSL_UPP_UPICR_WAITA_ENABLE << CSL_UPP_UPICR_WAITA_SHIFT) | //Using START signal
    		(CSL_UPP_UPICR_STARTA_ENABLE << CSL_UPP_UPICR_STARTA_SHIFT) | //Using START signal
    		(CSL_UPP_UPICR_ENAA_ENABLE << CSL_UPP_UPICR_ENAA_SHIFT) //Using ENA signal
    		;


    uppRegs->UPIVR = CSL_UPP_UPIVR_VALB_RESETVAL;

    uppRegs->UPTCR = 0; // 64byte transaction to DMAs

    uppRegs->UPDLB = CSL_UPP_UPDLB_BA_ENABLE << CSL_UPP_UPDLB_BA_SHIFT; //B to A loopback

    // 6. Program the uPP interrupt enable set register (UPIES) to interrupt generation for the desired
    //events. Register an interrupt service routine (ISR) if desired; otherwise, polling is required.
    uppRegs->UPIES = (0x1F << 8) | (0x1F); // Enable all interrupts, but no handling for them...


    // 7. Set the EN bit in the uPP peripheral control register (UPPCR) to 1 to turn on the uPP peripheral.
    uppRegs->UPPCR |= CSL_UPP_UPPCR_EN_ENABLE << CSL_UPP_UPPCR_EN_SHIFT;

    // 8 Optioanl : Cache operations. You don't have to do this because tx/rx buffers are in L2SRAM. This is just a place holder when moving tx/rx buffers to other memories which are not under control of cache controller.
    Cache_inv (g_rxBuffer, sizeof(g_rxBuffer), Cache_Type_ALL , TRUE);
    Cache_wbInv (g_txBuffer, sizeof(g_txBuffer), Cache_Type_ALL , TRUE);

    // 9. Setup DMAs

    // Wait pend bit is cleared before starting rx
    while (uppRegs->UPIS2 & CSL_UPP_UPIS2_PEND_MASK);

    // Start UPP Rx Part
    uppRegs->UPID0 = l2_global_address((UInt)g_rxBuffer);

    uppRegs->UPID1 = (1 << 16) | sizeof (g_rxBuffer);

    uppRegs->UPID2 = 0;


    // Wait pend bit is cleared before starting tx
    while (uppRegs->UPQS2 & CSL_UPP_UPQS2_PEND_MASK);

    // Start UPP Tx Part
    uppRegs->UPQD0 = l2_global_address((UInt)g_txBuffer);

    uppRegs->UPQD1 = (1 << 16) | sizeof (g_txBuffer);

    uppRegs->UPQD2 = 0;

    time0 = Timestamp_get32 ();

    // 10. wait for the transfer completion(s).
    while (
    		((uppRegs->UPISR & CSL_UPP_UPISR_EOWQ_MASK) == CSL_UPP_UPISR_EOWQ_FALSE) ||
    		((uppRegs->UPISR & CSL_UPP_UPISR_EOWI_MASK) == CSL_UPP_UPISR_EOWI_FALSE)
		) {
			asm (" nop");
    	}

    time1 = Timestamp_get32 ();

    if (time1 > time0) {
    	diff = time1 - time0;
    }
    else {
    	diff = time0 - time1;
    }

    System_printf ("UPP latency : %d cycle\n", diff - calibration);
    System_flush ();
}

/*
 *  ======== main ========
 */
Void main()
{
    BIOS_start();        /* enable interrupts and start SYS/BIOS */
}
