/*
 * Copyright 2018 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*****************************************************************************
*
* \file nxp_sja1124.c
*
* \author NXP
*
* \brief Functions for the SJA1124
*
*****************************************************************************/

#include <SJA1124_Drv/nxp_sja1124.h>

/*****************************************************************************
* FUNCTIONS
*****************************************************************************/

/* External declaration of SPI communication functions. */
/* SPI bi-directional transfer function. Sends numOfBytes bytes of data stored
   inside sendDataPtr buffer and stores received data into rcvDataPtr buffer.
   The SPI peripheral instance spiDevice is the same as in SJA1124_ConfigType. */
extern Std_ReturnType SPI_Send(uint8_t spiDevice, uint8_t numOfBytes, uint8_t* sendDataPtr, uint8_t* rcvDataPtr);
/* GPIO output function used for SW-controlled chip-select pin. Writes setClear
   level (true = high, false = low) to output pin. The GPIO port pinPort
   and number pinNumber are the same as in SJA1124_SpiChipSelectType. */
extern void GPIO_WritePin(uint8_t pinPort, uint8_t pinNumber, bool setClear);

/* Internal device configuration. */
SJA1124_ConfigType deviceConfigInternal;

/*
* \brief The function Init initializes configuration registers of the device.
*
* \param[in] uint8_t device             - Device number (0..3 in case of 4 devices).
* \param[in] SJA1124_ConfigType* config - Pointer to SJA1124 configuration set.
* \return Std_ReturnType                - E_OK: Send command has been accepted.
*                                         E_NOT_OK: Send command has not been accepted.
*/
Std_ReturnType SJA1124_Init(uint8_t device, const SJA1124_ConfigType* config)
{
  Std_ReturnType error;
  uint8_t sendBufferTemp[5];

  if((device >= SJA1124_NUM_OF_DEVICES) || (config == NULL))
  {
    return E_NOT_OK;
  }

  /* Store used SPI device for internal use. */
  deviceConfigInternal.spiDevice = config->spiDevice;

  /* Store used SPI chip-select settings for internal use. */
  deviceConfigInternal.spiChipSelect[device].pinPort = config->spiChipSelect[device].pinPort;
  deviceConfigInternal.spiChipSelect[device].pinNumber = config->spiChipSelect[device].pinNumber;

  sendBufferTemp[0] = config->deviceConfig[device].modeRegister;
  sendBufferTemp[1] = config->deviceConfig[device].pllCfgRegister;
  sendBufferTemp[2] = config->deviceConfig[device].int1EnRegister;
  sendBufferTemp[3] = config->deviceConfig[device].int2EnRegister;
  sendBufferTemp[4] = config->deviceConfig[device].int3EnRegister;

  error = SJA1124_RWRegister(device, false, SJA1124_MODE_ADDR, 5, sendBufferTemp, NULL);

  return error;
}

/*
* \brief The function RWRegister creates command to send using SPI driver send/receive function.
*        1st byte (address)   2nd byte (setting)    3rd byte etc.
*        Addr7..0             RW 0 0 0 DLC3..0      Data
*
* \param[in] uint8_t device       - Device number (0..3 in case of 4 devices).
* \param[in] bool rwRead          - true: Read register.
*                                   false: Write register.
* \param[in] uint8_t regAddress   - Number of first register to write.
* \param[in] uint8_t numOfBytes   - Number of data bytes to read or write.
* \param[in] uint8_t* sendDataPtr - Pointer to buffer with data to send.
*                                   If this pointer is NULL, data bytes with 0x00 value are sent.
* \param[out] uint8_t* rcvDataPtr - Pointer to buffer where received data are stored.
*                                   If this pointer is NULL, received data are ignored.
* \return Std_ReturnType          - E_OK: Send command has been accepted.
*                                   E_NOT_OK: Send command has not been accepted.
*/
Std_ReturnType SJA1124_RWRegister(uint8_t device, bool rwRead, uint8_t regAddress, uint8_t numOfBytes, uint8_t* sendDataPtr, uint8_t* rcvDataPtr)
{
  Std_ReturnType error;
  uint8_t sendBufferTemp[numOfBytes + 2];
  uint8_t rcvBufferTemp[numOfBytes + 2];
  uint8_t i;

  if((device >= SJA1124_NUM_OF_DEVICES) || (numOfBytes == 0))
  {
    return E_NOT_OK;
  }

  /* First byte - register address. */
  sendBufferTemp[0] = regAddress;
  /* Second byte - read/write and data length. */
  sendBufferTemp[1] = (rwRead ? 0x80u : 0x00u);
  sendBufferTemp[1] |= (numOfBytes - 1);
  /* Third byte and further - data bytes, if NULL, then all of them are 0. */
  if(sendDataPtr != NULL)
  {
    for(i = 0; i < numOfBytes; i++)
    {
      sendBufferTemp[i + 2] = sendDataPtr[i];
    }
  }
  else
  {
    for(i = 0; i < numOfBytes; i++)
    {
      sendBufferTemp[i + 2] = 0x00u;
    }
  }

  /* Send the command - activate chip-select, send command, deactivate chip-select. */
  /* Deactivate chip-select to ensure inactive chip-select state at the beginning of transmission. */
  //GPIO_WritePin(deviceConfigInternal.spiChipSelect[device].pinPort, deviceConfigInternal.spiChipSelect[device].pinNumber, true);
  //GPIO_WritePin(deviceConfigInternal.spiChipSelect[device].pinPort, deviceConfigInternal.spiChipSelect[device].pinNumber, false);
  error = SPI_Send(deviceConfigInternal.spiDevice, numOfBytes + 2, sendBufferTemp, rcvBufferTemp);
  //GPIO_WritePin(deviceConfigInternal.spiChipSelect[device].pinPort, deviceConfigInternal.spiChipSelect[device].pinNumber, true);

  /* Store received data if the pointer is provided. */
  if((rcvDataPtr != NULL) && (error == E_OK))
  {
    for(i = 0; i < numOfBytes; i++)
    {
      rcvDataPtr[i] = rcvBufferTemp[i + 2];
    }
  }

  return error;
}

/*
* \brief The function SetOperationalMode sets operational mode of the device to normal or sleep
*        (low power) mode. In low power mode all LIN master controllers and the PLL are disabled,
*        the power consumption is reduced, but the device can still detect remote wake-up via LIN
*        or wake-up by SPI command.
*
* \param[in] uint8_t device                     - Device number (0..3 in case of 4 devices).
* \param[in] SJA1124_OperationalModeType opMode - Operational mode of the device, use
*                                                 SJA1124_MODE_NORMAL for normal device mode
*                                                 and SJA1124_MODE_SLEEP for low power mode.
* \return Std_ReturnType                        - E_OK: Send command has been accepted.
*                                                 E_NOT_OK: Send command has not been accepted.
*/
Std_ReturnType SJA1124_SetOperationalMode(uint8_t device, SJA1124_OperationalModeType opMode)
{
  Std_ReturnType error;
  uint8_t sendBufferTemp[1];

  if(device >= SJA1124_NUM_OF_DEVICES)
  {
    return E_NOT_OK;
  }

  /* Set operational mode in MODE register. */
  sendBufferTemp[0] = (uint8_t)opMode << SJA1124_MODE_LOWPWRMODE_SHIFT;

  error = SJA1124_RWRegister(device, false, SJA1124_MODE_ADDR, 1, sendBufferTemp, NULL);

  if(error == E_OK)
  {
    /* Store current operational mode. */
    deviceConfigInternal.deviceConfig[device].modeRegister = (uint8_t)opMode << SJA1124_MODE_LOWPWRMODE_SHIFT;
  }

  return error;
}

/*
* \brief The function GetOperationalMode gets last set operational mode.
*
* \param[in] uint8_t device                       - Device number (0..3 in case of 4 devices).
* \param[out] SJA1124_OperationalModeType* opMode - Operational mode of the device, value is
*                                                   SJA1124_MODE_NORMAL for normal device mode
*                                                   and SJA1124_MODE_SLEEP for low power mode.
* \return Std_ReturnType                          - E_OK: Send command has been accepted.
*                                                   E_NOT_OK: Send command has not been accepted.
*/
Std_ReturnType SJA1124_GetOperationalMode(uint8_t device, SJA1124_OperationalModeType* opMode)
{
  if((device >= SJA1124_NUM_OF_DEVICES) || (opMode == NULL))
  {
    return E_NOT_OK;
  }

  /* Return stored current operational mode. */
  *opMode = (SJA1124_OperationalModeType)((deviceConfigInternal.deviceConfig[device].modeRegister & SJA1124_MODE_LOWPWRMODE_MASK) >> SJA1124_MODE_LOWPWRMODE_SHIFT);

  return E_OK;
}

/*
* \brief The function SetWakeupMode enables/disables interrupts when wake-up is detected.
*        It can also be used to clear wake-up status bits in INT1 register.
*
* \param[in] uint8_t device                - Device number (0..3 in case of 4 devices).
* \param[in] SJA1124_WakeupModeType wuMode - Enable or disable all wake-up interrupts
*                                            (SJA1124_WAKEUP_ENABLE, SJA1124_WAKEUP_DISABLE)
*                                            or clear wake-up interrupt register in case of
*                                            an interrupt occurred (SJA1124_WAKEUP_CLEAR).
* \return Std_ReturnType                   - E_OK: Send command has been accepted.
*                                            E_NOT_OK: Send command has not been accepted.
*/
Std_ReturnType SJA1124_SetWakeupMode(uint8_t device, SJA1124_WakeupModeType wuMode)
{
  Std_ReturnType error;
  uint8_t sendBufferTemp[1];

  if(device >= SJA1124_NUM_OF_DEVICES)
  {
    return E_NOT_OK;
  }

  switch(wuMode)
  {
    case SJA1124_WUMODE_ENABLE:
      sendBufferTemp[0] = 0x0Fu;

      error = SJA1124_RWRegister(device, false, SJA1124_INT1EN_ADDR, 1, sendBufferTemp, NULL);
      break;
    case SJA1124_WUMODE_DISABLE:
      sendBufferTemp[0] = 0x00u;

      error = SJA1124_RWRegister(device, false, SJA1124_INT1EN_ADDR, 1, sendBufferTemp, NULL);
      break;
    case SJA1124_WUMODE_CLEAR:
      sendBufferTemp[0] = 0x0Fu;

      error = SJA1124_RWRegister(device, false, SJA1124_INT1_ADDR, 1, sendBufferTemp, NULL);
      break;
    default:
      error = E_NOT_OK;
      break;
  }

  return error;
}

/*
* \brief The function GetWakeupReason reads if the device was woken up remotely by another device
*        on the LIN bus. The device is woken up either remotely via LIN bus or by 1-0 transition
*        on SCSN pin. Use SetWakeupMode function to enable interrupts when wake-up is detected.
*
* \param[in] uint8_t device                      - Device number (0..3 in case of 4 devices).
* \param[out] SJA1124_WakeupReasonType* wuReason - Wake-up reason of the device, either
*                                                  SJA1124_WAKEUP_BUS when it's woken up by other
*                                                  device on the bus, or SJA1124_WAKEUP_SPI when
*                                                  it's woken up by SPI command.
* \return Std_ReturnType                         - E_OK: Send command has been accepted.
*                                                  E_NOT_OK: Send command has not been accepted.
*/
Std_ReturnType SJA1124_GetWakeupReason(uint8_t device, SJA1124_WakeupReasonType* wuReason)
{
  Std_ReturnType error;
  uint8_t rcvBufferTemp[1];

  if((device >= SJA1124_NUM_OF_DEVICES) || (wuReason == NULL))
  {
    return E_NOT_OK;
  }

  /* Read INT1 register. */
  error = SJA1124_RWRegister(device, true, SJA1124_INT1_ADDR, 1, NULL, rcvBufferTemp);

  if(error == E_OK)
  {
    if((rcvBufferTemp[0] & 0x0Fu) != 0)
    {
      *wuReason = SJA1124_WAKEUP_BUS;
    }
    else
    {
      *wuReason = SJA1124_WAKEUP_SPI;
    }

    /* Clear INT1 register. */
    error = SJA1124_SetWakeupMode(device, SJA1124_WUMODE_CLEAR);
    if(error != E_OK)
    {
      return error;
    }
  }

  return error;
}

/*
* \brief The function GetStatus reads the status registers of the device.
*
* \param[in] uint8_t device              - Device number (0..3 in case of 4 devices).
* \param[out] SJA1124_StatusType* status - Pointer to structure where status data are stored.
* \return Std_ReturnType                 - E_OK: Send command has been accepted.
*                                          E_NOT_OK: Send command has not been accepted.
*/
Std_ReturnType SJA1124_GetStatus(uint8_t device, SJA1124_StatusType* status)
{
  Std_ReturnType error;
  uint8_t sendBufferTemp[2];
  uint8_t rcvBufferTemp[4];

  if(status == NULL)
  {
    return E_NOT_OK;
  }

  /* Read interrupt and status registers. */
  error = SJA1124_RWRegister(device, true, SJA1124_INT1_ADDR, 4, NULL, rcvBufferTemp);

  if(error == E_OK)
  {
    status->int1Register = rcvBufferTemp[0];
    status->int2Register = rcvBufferTemp[1];
    status->int3Register = rcvBufferTemp[2];
    status->statusRegister = rcvBufferTemp[3];

    sendBufferTemp[0] = rcvBufferTemp[0];
    sendBufferTemp[1] = rcvBufferTemp[1];

    /* Clear interrupt flags if any are set. */
    if((rcvBufferTemp[0] != 0) && (rcvBufferTemp[1] != 0))
    {
      error = SJA1124_RWRegister(device, false, SJA1124_INT1_ADDR, 2, sendBufferTemp, NULL);
    }
    else if(rcvBufferTemp[0] != 0)
    {
      error = SJA1124_RWRegister(device, false, SJA1124_INT1_ADDR, 1, &sendBufferTemp[0], NULL);
    }
    else if(rcvBufferTemp[1] != 0)
    {
      error = SJA1124_RWRegister(device, false, SJA1124_INT2_ADDR, 1, &sendBufferTemp[1], NULL);
    }

    if(error != E_OK)
    {
      return error;
    }
  }

  return error;
}
