#include <stdlib.h>
#include <stdio.h>
#include "ti_drivers_config.h"
#include "ti_board_config.h"

#include <drivers/i2c.h>
#include <drivers/gpio.h>
#include <kernel/dpl/AddrTranslateP.h>
#include <kernel/dpl/DebugP.h>
#include "ti_drivers_open_close.h"
#include "ti_board_open_close.h"
#include <drivers/hw_include/cslr_soc.h>

#include <kernel/dpl/DebugP.h>
#include <kernel/dpl/ClockP.h>
#include <kernel/dpl/AddrTranslateP.h>
#include <kernel/dpl/HwiP.h>
#include "ti_drivers_config.h"
#include "ti_drivers_open_close.h"
#include "ti_board_open_close.h"
#include <drivers/sciclient/include/tisci/tisci_protocol.h>
#include <drivers/sciclient/include/tisci/rm/tisci_rm_irq.h>
#include <drivers/soc/am64x_am243x/soc.h>
#include "hw_gpmc.h"

// TPIC2810 registers
#define TPIC2810_CMD_RD                 (0x11U)
#define TPIC2810_CMD_WR_SHIFT_REG       (0x11U)
#define TPIC2810_CMD_LOAD_OUTPUT        (0x22U)
#define TPIC2810_CMD_WR_IMMEDIATE       (0x44U)


// GPMC memory space address
#define GPMC_MEM_ADDRESS                0x340000UL

// gpio 0 pin definitions
#define TIMING_PIN                      16      // set gpio 16 (chip pin d4, mcu header pin 11) to output to indicate time interval start/end or start of irq handler
#define IRQ_PIN                         15      // set gpio 15 (chip pin e5, mcu header pin 10) to output to use to trigger interrupt on rising edge

typedef enum {
    Initialize,
    Shutdown
} Transition;

void driverInit( Transition direction );
void gpmcRegTest( void );

// ************************************************************
//
// main - program entry point, initialize/shutdown system/board/driver & call tests
//
// ************************************************************

int main()
{
    // all initialiation occurs here
    driverInit( Initialize );

    // perform enabled tests
    gpmcRegTest();

    // shutodown and exit
    driverInit( Shutdown );
    return 0;
}

// ************************************************************
//
// driverInit - program entry point, initialize/shutdown system/board/driver & call tests
//
// ************************************************************

void driverInit( Transition direction )
{
    switch( direction ) {
        case Initialize:
            System_init();
            Board_init();
            Drivers_open();
            Board_driversOpen();
            break;

        case Shutdown:
            Board_driversClose();
            Drivers_close();
            Board_deinit();
            System_deinit();
            break;

        default:
            DebugP_logError( "unknown configuration operation\n" );
    }
}

// ************************************************************
//
// gpmcRegTest - configure & test gpmc memory bus cycles
//
// ************************************************************

void gpmcRegTest( void )
{
    volatile uint32_t *gpmcDataAddr;
    uint32_t gpmcBaseAddr;
    uint32_t value;
    uint32_t *ptr;

    ptr = AddrTranslateP_getLocalAddr( 0x00108180 );
    *ptr = 0x01;

    // turn on the gpmc clock - no register access until this is done
    SOC_moduleClockEnable( TISCI_DEV_GPMC0, 1 );

    // get base address of gpmc using RAT translation
    gpmcBaseAddr = (uint32_t) AddrTranslateP_getLocalAddr( CSL_GPMC0_CFG_BASE );

    // get gpmc status, revision
    value = HW_RD_REG32( gpmcBaseAddr + GPMC_STATUS   );
    value = HW_RD_REG32( gpmcBaseAddr + GPMC_REVISION );

    // reset
    HW_WR_REG32(  gpmcBaseAddr + GPMC_SYSCONFIG, GPMC_SYSCONFIG_SOFTRESET_RESET << GPMC_SYSCONFIG_SOFTRESET_SHIFT );
    CSL_REG32_WR( gpmcBaseAddr + GPMC_SYSCONFIG, GPMC_SYSCONFIG_SOFTRESET_RESET << GPMC_SYSCONFIG_SOFTRESET_SHIFT );

    // wait until reset complete
    while( ( HW_RD_REG32( gpmcBaseAddr + GPMC_SYSSTATUS ) & GPMC_SYSSTATUS_RESETDONE ) != GPMC_SYSSTATUS_RESETDONE_RSTDONE );

    // config1 register settings (bus behavior)
    value = ( GPMC_CONFIG1_0_READMULTIPLE_RDMULTIPLE  << GPMC_CONFIG1_0_READMULTIPLE_SHIFT        )   // enable read burst mode if readtype is set for synchronous
          | ( GPMC_CONFIG1_0_READTYPE_RDSYNC          << GPMC_CONFIG1_0_READTYPE_SHIFT            )   // selects read synchronous
          | ( GPMC_CONFIG1_0_WRITEMULTIPLE_WRMULTIPLE << GPMC_CONFIG1_0_WRITEMULTIPLE_SHIFT       )   // enable write burst mode if writetype is set for synchronous
          | ( GPMC_CONFIG1_0_WRITETYPE_WRSYNC         << GPMC_CONFIG1_0_WRITETYPE_SHIFT           )   // selects write synchronous
          | ( GPMC_CONFIG1_0_DEVICESIZE_SIXTEENBITS   << GPMC_CONFIG1_0_DEVICESIZE_SHIFT          )   // attached device is 16 bits
          | ( GPMC_CONFIG1_0_DEVICETYPE_NORLIKE       << GPMC_CONFIG1_0_DEVICETYPE_SHIFT          )   // attached device NOR-like (random r/w)
          | ( GPMC_CONFIG1_0_MUXADDDATA_MUX           << GPMC_CONFIG1_0_MUXADDDATA_SHIFT          )   // address/data is muxed
          | ( GPMC_CONFIG1_0_TIMEPARAGRANULARITY_X2   << GPMC_CONFIG1_0_TIMEPARAGRANULARITY_SHIFT )   // ext clock is x2 states
          | ( GPMC_CONFIG1_0_GPMCFCLKDIVIDER_DIVBY4   << GPMC_CONFIG1_0_GPMCFCLKDIVIDER_SHIFT     );  // divide fclk by 4 (lets start slow)
    HW_WR_REG32( gpmcBaseAddr + GPMC_CONFIG1(0), value );

    // config2 register settings (cs timings)
//  value = ;
//  HW_WR_REG32( gpmcBaseAddr + GPMC_CONFIG2(0), value );

    // config3 register settings (adv timings)
//  value = ;
//  HW_WR_REG32( gpmcBaseAddr + GPMC_CONFIG3(0), value );

    // config4 register settings (we/oe timings)
//  value = ;
//  HW_WR_REG32( gpmcBaseAddr + GPMC_CONFIG4(0), value );

    // config5 register settings (rd/wr timings)
//  value = ;
//  HW_WR_REG32( gpmcBaseAddr + GPMC_CONFIG5(0), value );

    // config6 register settings (wr/cycle timings)
//  value = ;
//  HW_WR_REG32( gpmcBaseAddr + GPMC_CONFIG6(0), value );

    // config7 register settings (cs/mask/base addr)
    value = ( 0x000000UL & GPMC_CONFIG7_0_BASEADDRESS )
          | ( GPMC_CONFIG7_0_CSVALID_CSENABLED << GPMC_CONFIG7_0_CSVALID_SHIFT );
    HW_WR_REG32( gpmcBaseAddr + GPMC_CONFIG7(0), value );

    // drive bus cycles for timing verification
    gpmcDataAddr = (uint32_t *) AddrTranslateP_getLocalAddr( CSL_GPMC0_DATA_BASE );
    value        = 0x00000000;
    while( 1 ) {
        *gpmcDataAddr = value++;
       printf( "write\n" );
    }
}

