/*******************************************************************************
** COPYRIGHT NOTIFICATION (c) 2013 HMS Industrial Networks AB                 **
**                                                                            **
** This code is the property of HMS Industrial Networks AB.                   **
** The source code may not be reproduced, distributed, or used without        **
** permission. When used together with a product from HMS, this code can be   **
** modified, reproduced and distributed in binary form without any            **
** restrictions.                                                              **
**                                                                            **
** THE CODE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. HMS DOES NOT    **
** WARRANT THAT THE FUNCTIONS OF THE CODE WILL MEET YOUR REQUIREMENTS, OR     **
** THAT THE OPERATION OF THE CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR     **
** THAT DEFECTS IN IT CAN BE CORRECTED.                                       **
********************************************************************************
*/
#include "abcc_sys_adapt.h"
#include "abcc_sys_adapt_spi.h"
#include "../HardwareProfile.h"
#include "spi.h"


/******************************************************************************/
/* Variables Declarations                                                     */
/******************************************************************************/
/* Holds the information about SPI configuartion */
unsigned int SPI3CON1Value = 0;
unsigned int SPI3CON2Value = 0;
/* Holds the information about SPI Enable/Disable */
unsigned int SPI3STATValue = 0;

extern void Delay10us(DWORD dwCount);
#ifdef ABCC_USER_DRV_SPI
/*
** Callback for SPI data ready indication.
*/
static ABCC_SYS_SpiDataReceivedCbfType pnDataReadyCbf;
#endif


/***************************************************************************//**
 * @brief This function is called by the driver at the beginning of ABCC_StartDriver().
 * @details If needed, any hardware or system dependent initialization shall be done here.
 * Note that ABCC_StartDriver() will also be called during restart of the driver.
 * @param none.
 * @return true if success, false otherwise.
*******************************************************************************/
BOOL ABCC_SYS_Init( void )
{
    CloseSPI3();
    SPI3CON1Value = ENABLE_SCK_PIN & ENABLE_SDO_PIN & SPI_MODE16_OFF & SPI_SMP_OFF & SPI_CKE_OFF & SLAVE_ENABLE_OFF & MASTER_ENABLE_ON &
            CLK_POL_ACTIVE_LOW & SEC_PRESCAL_3_1 & PRI_PRESCAL_4_1;
    SPI3CON2Value = FRAME_ENABLE_OFF & FRAME_SYNC_OUTPUT & FRAME_POL_ACTIVE_LOW & FIFO_BUFFER_DISABLE;
    SPI3STATValue = SPI_ENABLE & SPI_IDLE_CON & SPI_RX_OVFLOW;
    OpenSPI3(SPI3CON1Value,SPI3CON2Value,SPI3STATValue );

    return TRUE;
}

/***************************************************************************//**
 * @brief Called from driver at the end of ABCC_ShutDown().
 * @details Note that the driver can be started again by calling ABCC_StartDriver().
 * @param none.
 * @return none.
*******************************************************************************/
void ABCC_SYS_Close( void )
{
    CloseSPI3();
}

/***************************************************************************//**
 * @brief Not use, Operating Mode fixed by HW.
 * @param bOpmode.
 * @return none.
*******************************************************************************/
void ABCC_SYS_SetOpmode( UINT8 bOpmode ) 
{
}
/***************************************************************************//**
 * @brief Return ABCC Operating Mode fixed by HW.
 * @return none.
*******************************************************************************/
UINT8 ABCC_SYS_GetOpmode( void )
{
   return ABP_OP_MODE_SPI;
}

/***************************************************************************//**
 * @brief Reset the module. Set the reset pin on the ABCC interface to low.
 * @return none.
*******************************************************************************/
void ABCC_SYS_HWReset( void )
{
    B40_RESET_IO = 0;
}

/***************************************************************************//**
 * @brief Release reset of the module. Sets the reset pin on the ABCC interface to high.
 * @return none.
*******************************************************************************/
void ABCC_SYS_HWReleaseReset( void )
{
    B40_RESET_IO = 1;
}

/***************************************************************************//**
 * @brief Read Module Identification pins on the host connector.
 * @details If the identification pins are not connected this function must return the correct
 *  value corresponding to the device.
 * 0 0 ( 0) Active CompactCom 30 series
 * 0 1 ( 1 ) Passive CompactCom
 * 1 0 ( 2 ) Active CompactCom 40 series
 * 1 1 ( 3 ) Customer specific
 * @param none.
 * @return none.
*******************************************************************************/
UINT8 ABCC_SYS_ReadModuleId( void )
{
    return 2;
}

/***************************************************************************//**
 * @brief Detects if a module is present by reading the Module Detection (MD) pins on the ABCC
 * interface.
 * @details If the detection pins are not connected this function must return true.
 * MD pin is internally connected to GND.
 * @return true if detected, false otherwise.
*******************************************************************************/
BOOL ABCC_SYS_ModuleDetect( void )
{
    if(B40_MD_IO == 0)
        return TRUE;
    else
        return FALSE;
}
/***************************************************************************//**
 * @brief Enable the ABCC HW interrupt (IRQ_N pin on the application interface).
 * @details This function will be called by the driver when the ABCC interrupt shall be enabled.
 * @return none.
*******************************************************************************/
void ABCC_SYS_AbccInterruptEnable( void )
{
    INTCON2bits.INT4EP = 1; //Interrupt on negative edge
    IEC3bits.INT4IE = 1;
    IFS3bits.INT4IF = 0;
}

/***************************************************************************//**
 * @brief Disable ABCC HW interrupt (IRQ_N pin on the application interface)
 * @return none.
*******************************************************************************/
void ABCC_SYS_AbccInterruptDisable( void )
{
    IEC3bits.INT4IE = 0;
}

/***************************************************************************//**
 * @brief This function is called by the driver when there is a possibility of internal resource
 * conflicts between the ABCC interrupt handler and the application thread or main loop.
 * @details The function temporary disables interrupts to avoid conflict. Note that all
 * interrupts that could lead to a driver access need to be disabled. If no interrupts
 * are used and the driver is accessed by a single thread or main loop there is no
 * need to implement this function. This function could also protect from conflicts
 * caused by different threads accessing the driver at the same time by disabling all
 * interrupts.
 * @return none.
*******************************************************************************/
void ABCC_SYS_EnterCriticalImpl( void )
{
}

/***************************************************************************//**
 * @brief Restore interrupts to the state when ABCC_SYS_EnterCritical() was called.
 * @return none.
*******************************************************************************/
void ABCC_SYS_ExitCriticalImpl( void )
{
}

/***************************************************************************//**
 * @brief If any preparation is needed before calling ABCC_SYS_EnterCritical() or
 * ABCC_SYS_ExitCritical(), this macro is used to add HW specific necessities.
 * @return none.
*******************************************************************************/
void ABCC_SYS_UseCriticalImpl( void )
{
}


#ifdef ABCC_USER_DRV_SPI

/***************************************************************************//**
 * @brief Registers the callback function that will be called when new data is received.
 * @return none.
*******************************************************************************/
void ABCC_SYS_SpiRegDataReceived( ABCC_SYS_SpiDataReceivedCbfType pnDataReceived  )
{
   pnDataReadyCbf = pnDataReceived;
}

/***************************************************************************//**
 * @brief Handles sending and receiving data in SPI mode.
 * @param pxSendDataBuffer - Pointer to MOSI Buffer to send
 * @param pxReceiveDataBuffer - Pointer to MISO Buffer.
 * @param iLength - Length of SPI frame ( in bytes )
 * @return none.
*******************************************************************************/
void ABCC_SYS_SpiSendReceive( void* pxSendDataBuffer, void* pxReceiveDataBuffer, UINT16 iLength )
{
    unsigned char byte = 0;
    /*
    ** Note: Needs to be a blocking call in this implementation.
    */
    //pnDataReadyCbf();

    ABCC_SYS_UseCritical();
    ABCC_SYS_EnterCritical();
    B40_CS_IO=0;
    for (byte = 0; byte < iLength; byte++) {
        WriteSPI3(*((BYTE *)pxSendDataBuffer));
        pxSendDataBuffer = (BYTE *)pxSendDataBuffer + 1;
        while (SPI3STATbits.SPITBF);
        while (!DataRdySPI3());
        *((BYTE *)pxReceiveDataBuffer) = ReadSPI3();
        pxReceiveDataBuffer = (BYTE *)pxReceiveDataBuffer + 1;
    }
    B40_CS_IO=1;
    ABCC_SYS_ExitCritical();
    pnDataReadyCbf();
}
#endif


/*******************************************************************************
** End of sys_adapt.cpp
********************************************************************************
*/
