/*******************************************************************************
 * (c) Copyright 2015 Microsemi Corporation.  All rights reserved.
 *
 *
 *
 * SVN $Revision: $
 * SVN $Date: $
 */

/*!
 *\file
 *\brief
 *		 Hardware Abstraction Layer functions for accessing Metroam Hardware
 */

/******************************************************************************
 * Include Files:
 ******************************************************************************/
#include <api_oam_sdk/common/error_codes/inc/mr_error_codes.h>
#include <api_oam_sdk/metroam_hal/inc/mr_hal.h>
#include "osal.h"
#include "egl_assert.h"
#include "egl_log.h"
/******************************************************************************
 * Global Parameters
 ******************************************************************************/
//VR Updated for PF OAM design
//

static uint32_t 	*device_base_address; 	// A pointer used to address the base address of the logic in the fabric

static osMutex_t	g_HAL_ind_mem_mutex		= NULL;						// A Mutex used to protect indirect RAM access to the logic in the fabric
static uint16_t		log_handle				= 0;						// handle used to log messages for this module

uint32_t			accumulatedTime	= 0;		// Tick counts (1ms) elapsed during indirect reads/writes
uint32_t			totalIndAccCount = 0;		// A count of the number of indirect read/write accesses since system start
int print_reg = 1;
/******************************************************************************
 * See mr_hal.h for details of how to use this function.
 ******************************************************************************/
static void requestMutex(void)
{
    // Access to MetRoam HW for RAM reads is managed by use of a mutex.
    osMutexTake(g_HAL_ind_mem_mutex, 0);

    // Disable interrupts from HW in fabric if the call is not made by the ISR
    osDisableIRQ(OS_OAM_IRQ);
}

/******************************************************************************
 * See mr_hal.h for details of how to use this function.
 ******************************************************************************/
static void releaseMutex(void)
{
    // Release the MetRoam HW semaphore.
    // Re-enable interrupts from HW in fabric
    osEnableIRQ(OS_OAM_IRQ);

    // Release the HAL mutex if NOT called within the ISR
    osMutexGive(g_HAL_ind_mem_mutex);

}

/*
 * This function is used to change the lowest level severity to log for
 * this module. Values of eLOG_LEVEL_DEBUG (lowest) to eLOG_LEVEL_ERROR
 * can be selected. Levels with greater severity than eLOG_LEVEL_ERROR
 * will default to eLOG_LEVEL_ERROR since severity of eLOG_LEVEL_ERROR
 * and higher are non maskable.
 */
void setMrHalLogLevel(log_level_t logLevel)
{
    setModuleLogLevel(log_handle,logLevel);
}

/******************************************************************************
 * See mr_hal.h for details of how to use this function.
 ******************************************************************************/
void mrHalInit(uint32_t cpuif_metroam_add)
{

    // Get a mutex to protect indirect access if we don't already have one
    if (g_HAL_ind_mem_mutex == NULL)
        g_HAL_ind_mem_mutex = osMutexCreate();

    ASSERT_NOT_NULL(g_HAL_ind_mem_mutex);

    log_init();
    device_base_address = (uint32_t*)cpuif_metroam_add;
    // Get a log handle if we don't already have one
    if (log_handle == 0)
        log_handle = log_register("MR_HAL");



}

/******************************************************************************
 * See mr_hal.h for details of how to use this function.
 ******************************************************************************/
void mrHalRegRead(uint16_t regAddr, uint32_t *rdData)
{
    volatile uint32_t * registerPtr = device_base_address;
    registerPtr += regAddr >> 2;

    // Read the value from the register
    *rdData = *registerPtr;
}

/******************************************************************************
 * See mr_hal.h for details of how to use this function.
 ******************************************************************************/
void mrHalRegWrite(uint16_t regAddr, uint32_t wrData)
{
    volatile uint32_t * registerPtr = device_base_address;
    registerPtr += regAddr >> 2;

    // Write the value to the register
    *registerPtr = wrData;
    print_reg = 0; //VR
    if(print_reg == 1)
    {
        ts_printf( "mrHalRegWrite:registerPtr 0x%0x (wrData=0x%0x):\n",registerPtr,wrData);
        print_reg = 0;
    }

}


/******************************************************************************
 * See mr_hal.h for details of how to use this function.
 ******************************************************************************/
void mrHalRamRead(uint32_t ramAddr, indirectRamSel_t selRam, uint32_t *rdData)
{
    uint32_t  data;
    uint32_t  indCtrl;
    uint16_t  loopCnt;

    uint32_t	start;
    start = osGetTickCount();

    // Request the HW semaphore before accessing the indirect RAM.
    requestMutex();
    //ts_printf("mrHalRamRead:requestMutex\n\r");

    // Write the Address, selRAM and rd/wr bit to the Indirect Control Register
    indCtrl = IND_ACC_INIT_MASK + IND_READ_MASK + (((uint32_t) selRam) << 22) + ramAddr;
    //ts_printf("mrHalRegWrite:indCtrl=%d\n\r",indCtrl);
    mrHalRegWrite((uint16_t) IND_CTRL_REG_ADDR,indCtrl);

    // Stay polling the indirect control register while the IND_ACC_INIT_MASK is set
    // HW will clear this bit when the read data is ready.
    mrHalRegRead((uint16_t)IND_CTRL_REG_ADDR, &indCtrl);
    //ts_printf("mrHalRamRead: indCtrl=%d\n\r",indCtrl);
    loopCnt = 0;
    while((indCtrl & IND_ACC_INIT_MASK) && (loopCnt++ < IND_MAX_POLL))
    {
        mrHalRegRead((uint16_t)IND_CTRL_REG_ADDR, &indCtrl);
       // ts_printf("mrHalRamRead: indCtrl=%d\n\r",indCtrl);
    }

    if (loopCnt == IND_MAX_POLL)
    {
       // ts_printf("mrHalRamRead:Indirect Read from ram %d at address 0x%08lX timed out\n",selRam,ramAddr);
    }

    EGL_ASSERT(loopCnt < IND_MAX_POLL, MR_ERR_READING_RAM);

    // Read the data
    mrHalRegRead((uint16_t)IND_RD_DATA_REG_ADDR, &data);

    // Release the HW Mutex when finished...
    releaseMutex();

    // set the returned rdData value to the data value read.
    *rdData = data;

    accumulatedTime += osGetTickCount() - start;
    totalIndAccCount++;
#ifdef DEBUG_PRINT_L2
    ts_printf(".");
#endif
}

/******************************************************************************
 * See mr_hal.h for details of how to use this function.
 ******************************************************************************/

void mrHalRamWrite(uint32_t ramAddr, indirectRamSel_t selRam, uint32_t wrData)
{
    uint32_t	indCtrl;
    uint16_t	loopCnt;

    uint32_t	start;
    start = osGetTickCount();

    // Request the HW semaphore before accessing the indirect RAM.
    requestMutex();

    // Write the data to the indirect write data register
    mrHalRegWrite((uint16_t) IND_WR_DATA_REG_ADDR,wrData);

    // Write the Address, selRAM and rd/wr bit to the Indirect Control Register
    indCtrl = IND_ACC_INIT_MASK + IND_WRITE_MASK + (((uint32_t) selRam)<<22) + ramAddr;
    mrHalRegWrite((uint16_t) IND_CTRL_REG_ADDR,indCtrl);
    print_reg = 0; //VR
    if(print_reg == 1)
    {
        ts_printf( "mrHalRamWrite:IND_CTRL_REG_ADDR 0x5000 (indCtrl=0x%0x):\n",indCtrl);
        print_reg = 0;
    }
    // Stay polling the indirect control register while the IND_ACC_INIT_MASK is set
    // HW will clear this bit when the read data is ready.
    mrHalRegRead((uint16_t)IND_CTRL_REG_ADDR, &indCtrl);
    loopCnt = 0;
    while((indCtrl & IND_ACC_INIT_MASK) && (loopCnt++ < IND_MAX_POLL))
    {
        mrHalRegRead((uint16_t)IND_CTRL_REG_ADDR, &indCtrl);
    }

    if (loopCnt == IND_MAX_POLL)
        ts_printf("Indirect Write to ram %d at address 0x%08lX timed out\n",selRam,ramAddr);

    EGL_ASSERT(loopCnt < IND_MAX_POLL, MR_ERR_WRITING_RAM);

    // Release the HW Mutex when finished...
    releaseMutex();

    accumulatedTime += osGetTickCount() - start;
    totalIndAccCount++;
}

