/***************************************************************************//**
 * @file
 * @brief Board Controller Communications (BCC) definitions
 * @version 5.1.3
 *******************************************************************************
 * @section License
 * <b>Copyright 2015 Silicon Labs, Inc. http://www.silabs.com</b>
 *******************************************************************************
 *
 * This file is licensed under the Silabs License Agreement. See the file
 * "Silabs_License_Agreement.txt" for details. Before using this software for
 * any purpose, you must agree to the terms of that agreement.
 *
 ******************************************************************************/



#include <string.h>
#include "em_device.h"
#include "em_cmu.h"
#include "em_gpio.h"

#include "bsp.h"

#if defined( BSP_BCC_LEUART )
#include "em_leuart.h"
#else
#include "em_usart.h"
#endif

#if defined( BSP_STK )

/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */

/* Module local variables */
static uint32_t rxByteCount;
static uint32_t txByteCount;

/* Module local prototypes */
static void    TxByte( uint8_t data );
static uint8_t RxByte( void );

/** @endcond */

/***************************************************************************//**
 * @addtogroup BSP
 * @{
 ******************************************************************************/

/***************************************************************************//**
 * @addtogroup BSP_STK API for STKs and WSTKs
 * @{
 ******************************************************************************/

/**************************************************************************//**
 * @brief Deinitialize board controller communication support (BCC)
 *        functionality. Reverse actions performed by @ref BSP_BccInit().
 *
 * @return @ref BSP_STATUS_OK.
 *****************************************************************************/
int BSP_BccDeInit( void )
{
  /* Reset counters */
  rxByteCount = 0xFFFFFFFFUL;
  txByteCount = 0xFFFFFFFFUL;

  BSP_BccPinsEnable( false );

#if defined( BSP_BCC_LEUART )
  /* Reset LEUART */
  LEUART_Reset( BSP_BCC_LEUART );
#else
  /* Reset USART */
  USART_Reset( BSP_BCC_USART );
#endif

  /* Disable clock */
  CMU_ClockEnable( BSP_BCC_CLK, false );

  return BSP_STATUS_OK;
}

/**************************************************************************//**
 * @brief Initialize board controller communication support (BCC)
 *        functionality.
 *
 * @return @ref BSP_STATUS_OK.
 *****************************************************************************/
int BSP_BccInit( void )
{
#if defined( BSP_BCC_LEUART )
  LEUART_Init_TypeDef leuartInit = LEUART_INIT_DEFAULT;
#else
  USART_InitAsync_TypeDef usartInit = USART_INITASYNC_DEFAULT;
#endif

  rxByteCount = 0;
  txByteCount = 0;

  /* Enable High Frequency Peripherals */
  CMU_ClockEnable(cmuClock_HFPER, true);

  /* Enable clocks to GPIO */
  CMU_ClockEnable(cmuClock_GPIO, true);

  /* Enable UART clock */
  CMU_ClockEnable( BSP_BCC_CLK, true );

#if defined( BSP_BCC_LEUART )
  /* Enable CORE LE clock in order to access LE modules */
  CMU_ClockEnable(cmuClock_CORELE, true);

  /* Select CORE LE clock for LE modules */
  CMU_ClockSelectSet( cmuClock_LFB, cmuSelect_CORELEDIV2 );

   /* Initialize LEUART */
   leuartInit.baudrate = 115200;
   LEUART_Init( BSP_BCC_LEUART, &leuartInit );
#else
  /* Initialize USART */
  USART_InitAsync( BSP_BCC_USART, &usartInit );
#endif

  /* Initialize UART pins */
  BSP_BccPinsEnable( true );

  return BSP_STATUS_OK;
}

/**************************************************************************//**
 * @brief Get a packet from the board controller.
 *
 * @param[in] pkt Pointer to a @ref BCP_Packet instance.
 *
 * @return True if packet received without errors, false otherwise.
 *****************************************************************************/
bool BSP_BccPacketReceive( BCP_Packet *pkt )
{
  int i;
  int length;
  uint8_t *bptr;

  /* Setup a byte pointer to start of packet buffer */
  bptr   = (uint8_t *) pkt;

  /* Receive packet magic */
  *bptr++ = RxByte();
  if (pkt->magic != BSP_BCP_MAGIC)
  {
     return false;
  }

  /* Receive packet type */
  *bptr++ = RxByte();
  if ( (pkt->type < BSP_BCP_FIRST) || (pkt->type > BSP_BCP_LAST) )
  {
     return false;
  }

  /* Receive packet length */
  *bptr++ = RxByte();
  if (pkt->payloadLength > BSP_BCP_PACKET_SIZE)
  {
     return false;
  }

#if ( BSP_BCP_VERSION == 2 )
   /* Receive reserved byte */
   *bptr++ = RxByte();
#endif

  /* Receive packet data length field and sanity check it */
  length  = pkt->payloadLength;
  if (length > BSP_BCP_PACKET_SIZE)
  {
     length = BSP_BCP_PACKET_SIZE;
  }

  /* Receive packet payload */
  for( i=0; i<length; i++ )
  {
     *bptr++ = RxByte();
  }

  return true;
}

/**************************************************************************//**
 * @brief Send a packet to the board controller.
 *
 * @param[in] pkt Pointer to a @ref BCP_Packet instance.
 *
 * @return @ref BSP_STATUS_OK.
 *****************************************************************************/
int BSP_BccPacketSend( BCP_Packet *pkt )
{
  int i;

  /* Apply magic */
  pkt->magic = BSP_BCP_MAGIC;

  /* Transmit packet magic */
  TxByte( pkt->magic );

  /* Transmit packet type */
  TxByte( pkt->type );

  /* Transmit packet length */
  TxByte( pkt->payloadLength );

#if ( BSP_BCP_VERSION == 2 )
  /* Transmit reserved byte */
  TxByte( pkt->reserved );
#endif

  /* Transmit packet payload */
  for ( i=0; i<pkt->payloadLength; i++ )
  {
    TxByte( pkt->data[i] );
  }

  return BSP_STATUS_OK;
}

/**************************************************************************//**
 * @brief Enable GPIO pins for the USART/LEUART used for board communication.
 *
 * @param[in] enable Set to true to enable pins, set to false to disable.
 *****************************************************************************/
void BSP_BccPinsEnable( bool enable )
{
  if (enable)
  {
    /* Configure GPIO pin for UART TX */
    /* To avoid false start, configure output as high. */
    GPIO_PinModeSet( BSP_BCC_TXPORT, BSP_BCC_TXPIN, gpioModePushPull, 1 );

    /* Configure GPIO pin for UART RX */
    GPIO_PinModeSet( BSP_BCC_RXPORT, BSP_BCC_RXPIN, gpioModeInput, 1 );

#if defined(BSP_BCC_ENABLE_PORT)
      /* Enable the switch that enables UART communication. */
      GPIO_PinModeSet( BSP_BCC_ENABLE_PORT, BSP_BCC_ENABLE_PIN, gpioModePushPull, 1 );
#endif 
     
#if defined( BSP_BCC_LEUART )
    BSP_BCC_LEUART->ROUTE |= LEUART_ROUTE_RXPEN | LEUART_ROUTE_TXPEN | BSP_BCC_LOCATION;
#else

#if defined( USART_ROUTEPEN_TXPEN )
    BSP_BCC_USART->ROUTEPEN = USART_ROUTEPEN_TXPEN | USART_ROUTEPEN_RXPEN;
    BSP_BCC_USART->ROUTELOC0 =
      ( BSP_BCC_USART->ROUTELOC0 &
        ~( _USART_ROUTELOC0_TXLOC_MASK | _USART_ROUTELOC0_RXLOC_MASK ) )
      | ( BSP_BCC_TX_LOCATION  << _USART_ROUTELOC0_TXLOC_SHIFT  )
      | ( BSP_BCC_RX_LOCATION  << _USART_ROUTELOC0_RXLOC_SHIFT  );
#else
    BSP_BCC_USART->ROUTE |= USART_ROUTE_RXPEN | USART_ROUTE_TXPEN | BSP_BCC_LOCATION;
#endif
#endif
  }
  else
  {
#if defined(BSP_BCC_ENABLE_PORT)
    GPIO_PinModeSet( BSP_BCC_ENABLE_PORT, BSP_BCC_ENABLE_PIN, gpioModeDisabled, 0 );
#endif
#if defined( BSP_BCC_LEUART )
      BSP_BCC_LEUART->ROUTE &= ~(LEUART_ROUTE_RXPEN | LEUART_ROUTE_TXPEN);
#else
#if defined( USART_ROUTEPEN_TXPEN )
      BSP_BCC_USART->ROUTEPEN &= ~(USART_ROUTEPEN_TXPEN | USART_ROUTEPEN_RXPEN);
#else
      BSP_BCC_USART->ROUTE &= ~(USART_ROUTE_RXPEN | USART_ROUTE_TXPEN);
#endif
#endif

    GPIO_PinModeSet( BSP_BCC_TXPORT, BSP_BCC_TXPIN, gpioModeDisabled, 0 );

    GPIO_PinModeSet( BSP_BCC_RXPORT, BSP_BCC_RXPIN, gpioModeDisabled, 0 );
  }
}

/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */

static uint8_t RxByte( void )
{
  uint8_t byte;

  /* Poll RX data available flag and return a character when one is available */

#if defined( BSP_BCC_LEUART )
  while (!(BSP_BCC_LEUART->IF & LEUART_IF_RXDATAV)) ;
  byte = BSP_BCC_LEUART->RXDATA;
#else
  while (!(BSP_BCC_USART->STATUS & USART_STATUS_RXDATAV)) ;
  byte = BSP_BCC_USART->RXDATA;
#endif

  rxByteCount++;
  return byte;
}

static void TxByte( uint8_t data )
{
  /* Check TX buffer and allow for a pending transfer to complete */

#if defined( BSP_BCC_LEUART )
  while (!(BSP_BCC_LEUART->STATUS & LEUART_STATUS_TXBL)) ;
  BSP_BCC_LEUART->TXDATA = (uint32_t) data;
#else
  while (!(BSP_BCC_USART->STATUS & USART_STATUS_TXBL)) ;
  BSP_BCC_USART->TXDATA = (uint32_t) data;
#endif

  txByteCount++;
}

/** @endcond */

/** @} (end group BSP_STK) */
/** @} (end group BSP) */

#endif /* BSP_STK */
