/*
 * 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 SJA1124_test.c
** @version 01.00
** @brief
**         SJA1124 BSP API and test API
*/
/*!
**  @addtogroup main_module main module documentation
**  @{
*/
/* MODULE TJA1124_test */

/* Including needed modules to compile this module/procedure */
#include <SJA1124_Drv/nxp_sja1124_lin.h>
#include "bsp.h"
#include "lin_sja1124_test.h"

/* User includes (#include below this line is not maintained by Processor Expert) */

/*use EMIOS2 channel 0 to generate a 2MHz PWM as SJA1124 CLK input*/
#define SJA1124_CLKGEN_EMIOS_INST 2
#define SJA1124_CLKGEN_EMIOS_CH 0

/* Boards used: S32K344EVB-WB + SJA1124EVB */
/* S32K344 pins used in this application:
 * SJA1124 SPI
 * - PTA17 [out] - LPSPI3_MOSI -> SJA_SDI
 * - PTF12 [in] - LPSPI3_MISO  -> SJA_SDO
 * - PTF13 [out] - LPSPI3_SCLK -> SJA_SCK
 * - PTB17 [out] - GPIO       -> SJA_SCSN (SW controlled)
 *
 * SJA1124 other pins
 * - PTB8 [in] - GPIO INT     -> SJA_INTN
 * - PTG12 [in] - GPIO        -> SJA_STAT
 * - PTD4 [out] - GPIO        -> SJA_TMF
 * - PTF0 [out] - EMISO2_CH0  -> SJA_CLK
 */

#define SJA1124_SPI_INST  3U
#define SJA1124_SPI_externalDevice  &Lpspi_Ip_DeviceAttributes_SPI3_PCS0_SJA1124_Instance_3_BOARD_InitPeripherals

/* User definitions. */
#define SPI_TIMEOUT 100000u

/* Enumeration used for SCSN pin port. */
typedef enum
{
   gpioPortA = 0u,  /* PORT A. */
   gpioPortB,       /* PORT B. */
   gpioPortC,       /* PORT C. */
   gpioPortD,       /* PORT D. */
   gpioPortE,       /* PORT E. */
   gpioPortF,        /* PORT F. */
   gpioPortG        /* PORTF. */

} Gpio_PortT;


/* SPI SCSN pin configuration - pin PTB17. */
const SJA1124_SpiChipSelectType spiChipSelect0 = {
   .pinPort = gpioPortB,
   .pinNumber = 17u,
 };
 /* Device configuration - PLL multiplier configuration, enabling interrupts. */
const SJA1124_DeviceConfigType sja1124DeviceConfig = {
   .modeRegister = 0x00u,
   .pllCfgRegister = 0x05u,
   .int1EnRegister = 0x0Fu,
   .int2EnRegister = 0x3Eu,
   .int3EnRegister = 0xFFu,
 };
 /* SJA1124 driver configuration - SPI device, SCSN pin, device configuration. */
 SJA1124_ConfigType sja1124DriverConfig = {
   .spiDevice = SJA1124_SPI_INST,
   .spiChipSelect[0] = spiChipSelect0,
   .deviceConfig[0] = sja1124DeviceConfig,
 };
 /* LIN channel configuration. */
 const Lin_ChannelConfigType linChannelConfig = {
   .checksumCalcDisable = false,         /* Hardware checksum calculation enabled. */
   .masterBreakLength = LIN_13BIT_BREAK, /* 13 bit break length. */
   .twoBitDelimiter = true,              /* 2 bit delimiter length. */
   .idleOnBitError = true,               /* Bit error resets LIN state machine. */
   .idleOnTimeout = true,                /* Idle on timeout event resets LIN state machine. */
   .stopBits = true,                     /* 2 stop bits. */
   .responseTimeout = 0x0Eu,             /* Response timeout - 1.4x minimal response time. */
   /* Baudrate calculated from input clock (CLK pin - MCU CLKOUT 2 MHz) and PLLMULT. */
   /*
    * baudrate = PLL_CLK/(16 * intBaudrate + fracBaudrate)
    *          = 2MHz* 15/(16 * 13 + 0)
    *          = 9615 baud/s
    */
   .fracBaudrate = 0x05u,                /* Fractional baudrate. */
   .intBaudrate = 0xC3u,                 /* Integer baudrate. */
   .lie = 0xF7u,                         /* LIN interrupt enable register. */
 };
 /* LIN configuration - number of channels, channels configuration (all the same). */
 Lin_ConfigType linConfig = {
   .maxHWChannels = 4u,
   .linChannel[0] = linChannelConfig,
   .linChannel[1] = linChannelConfig,
   .linChannel[2] = linChannelConfig,
   .linChannel[3] = linChannelConfig,
 };
 /* Frame configuration. */
 Lin_PduType linFrame_Master_Response = {
   .id = 0x2Au,                /* Identifier - ID. */
   .dfl = 7u,                  /* Data field length = number of data bytes - 1. */
   .dir = LIN_MASTER_RESPONSE, /* Direction - master to slave. */
   .ccs = LIN_ENHANCED_CS,     /* Enhanced checksum. */
   .cf = 0x40u,                /* Checksum - not needed, checksum calculated automatically. */
   .data = { 0xFFu, 0x00u, 0xFFu, 0x00u, 0xFFu, 0x00u, 0xFFu, 0x00u }, /* Data to send. */
 };

 Lin_PduType linFrame_Slave_Response = {
     .id = 0x1Au,                /* Identifier - ID. */
     .dfl = 7u,                  /* Data field length = number of data bytes - 1. */
     .dir = LIN_SLAVE_RESPONSE, /* Direction - master to slave. */
     .ccs = LIN_ENHANCED_CS,     /* Enhanced checksum. */
     .cf = 0x40u,                /* Checksum - not needed, checksum calculated automatically. */
     .data = { 0 }, /* buffer for receive response from slave, clean it when initialization */
   };

/* Definition of the GPIO_ReadPin function. */
bool GPIO_ReadPin(uint8_t pinPort, uint8_t pinNumber)
{
	(void)pinPort;
	(void)pinNumber;
	return (bool) (Siul2_Dio_Ip_ReadPin(SJA1124_CS_PORT, SJA1124_CS_PIN));
}

/* Definition of the GPIO_WritePin function used externally by SJA1124 driver. */
void GPIO_WritePin(uint8_t pinPort, uint8_t pinNumber, bool setClear)
{
	(void)pinPort;
	(void)pinNumber;
	Siul2_Dio_Ip_WritePin(SJA1124_CS_PORT, SJA1124_CS_PIN, setClear);
}

/* Definition of the SPI_Send function used externally by SJA1124 driver. */
Std_ReturnType SPI_Send(uint8_t spiDevice, uint8_t numOfBytes, uint8_t* sendDataPtr, uint8_t* rcvDataPtr)
{
	Std_ReturnType ret;
	(void)spiDevice;
	ret = Lpspi_Ip_SyncTransmit(SJA1124_SPI_externalDevice, sendDataPtr, rcvDataPtr, numOfBytes, SPI_TIMEOUT);
	return ret;
}

/********************************************************************
 * This function initializes LPSPI, EMIOS_PWM, GPIO and SJA1124 driver
 * for SJA1124 test:
 *    1. initialize LPSPI2 with SW SS low active, SCLK idle low and speed of 2Mbit/s
 *    2. initialize EMIOS2 module channel 0 to generate a 2MHz PWM as SJA1124 reference clock
 *    3. initialize PTB17 as GPIO input to monitor SJA1124 command execution status
 *    4. initialize SJA1124 with customized user configuration
 *
 *******************************************************************/
Std_ReturnType SJA1124_LIN_Comm_Init(void)
{
	SJA1124_StatusType deviceStatus; /* Status of device - INT1..3, STATUS registers. */

	Std_ReturnType status = E_NOT_OK;

	/* Initialize LPSPI3 with PCS0
	* - Baudrate       2MHz
	* - Clock phase    Second edge
	* - Clock polarity Active high
	* - Direction      MSB first
	*/

	/* If LPSPI3 is not initialized in BSP_Init(), call it here */
	/* This is already called in Bsp_Init(); so we remove this line here. */
	/* Lpspi_Ip_Init(&Lpspi_Ip_PhyUnitConfig_SpiPhyUnit_3_Instance_3_BOARD_InitPeripherals); */

	/*use EMIOS2 channel 0 to generate a 2MHz PWM as SJA1124 CLK input*/

	/* If this is not called in BSP_init(), call it here */
	Emios_Mcl_Ip_Init(EMIOS2_INST, &Emios_Mcl_Ip_2_Config_BOARD_INITPERIPHERALS);

	Emios_Pwm_Ip_InitChannel(SJA1124_CLKGEN_EMIOS_INST, &Emios_Pwm_Ip_BOARD_InitPeripherals_I2_Ch0);

	/* Device initialization. */
	SJA1124_Init(0, &sja1124DriverConfig);

	/* LIN channels initialization. */
	Lin_Init(0, &linConfig);

	/* Read device status. */
	status = SJA1124_GetStatus(0, &deviceStatus);

	status |= SJA1124_LIN_Read_Id();

	return status;
}
/********************************************************************
 * This function calls SJA1124 driver APIs to test SJA1124 LIN channel
 * both master response ID frame and slave response ID frame as tested for each LIN channel
 *      master response ID = 0x2A
 *      slave response ID = 0x1A
 *      enhanced CRC is used and data length = 8
 *      13-break is used with 9600 baud rate
 * Note: SJA1124 LIN controller supports only master node, so it will always send out
 *       the LIN frame header(break + sync + PID field)
 *******************************************************************/
void SJA1124_LIN_Comm_Test(void)
{
	/* Write your local variable definition here */

	Lin_SduType linStatus;           /* Status of LIN channel. */
	bool frameSent = false;          /* Frame sent flag. */
	bool frameSentError[4] = {false, false, false, false}; /* Error flags for each channel. */
	uint8_t channel;

	/* Send LIN frames on all channels one after another. */
	for(channel = 0; channel < 4; channel++)
	{
		/* If frame error was previously detected, clear error. */
		if(frameSentError[channel] == true)
		{
			/* Read and clear error. */
			Lin_GetStatus(channel, &linFrame_Master_Response, &linStatus);

			/* Clear frame error flag if error has been cleared. */
			if(linStatus.les == 0)
			{
				frameSentError[channel] = false;
			}

			/* Read and clear error. */
			Lin_GetStatus(channel, &linFrame_Slave_Response, &linStatus);

			/* Clear frame error flag if error has been cleared. */
			if(linStatus.les == 0)
			{
				frameSentError[channel] = false;
			}
		}

		/* Send LIN frame, if no error is pending. */
		if(frameSentError[channel] == false)
		{

			/**send LIN message frame with master node response**/
			frameSent = false;
			Lin_SendFrame(channel, &linFrame_Master_Response, true);

			/* LIN channel status read after INTN pin is assigned.*/
			/* Status could also be read in interrupt after INTN pin is assigned. */
			while((frameSent == false) && (frameSentError[channel] == false))
			{
				/* Wait until INTN is assigned. */
				if(Siul2_Dio_Ip_ReadPin(SJA1124_INT_PORT, SJA1124_INT_PIN) == false)
				{
					/* Check if the frame is sent. */
					if(Lin_GetStatus(channel, &linFrame_Master_Response, &linStatus) == LIN_TX_OK)
					{
						frameSent = true;
					}
					/* Check if frame error is detected. */
					else if(linStatus.les != 0)
					{
						frameSentError[channel] = true;
					}
				}
			}

			/**send LIN message frame with slave node response**/
			frameSent = false;
			Lin_SendFrame(channel, &linFrame_Slave_Response, true);

			/* LIN channel status read after INTN pin is assigned.*/
			/* Status could also be read in interrupt after INTN pin is assigned. */
			while((frameSent == false) && (frameSentError[channel] == false))
			{
				/* Wait until INTN is assigned. */
				if(Siul2_Dio_Ip_ReadPin(SJA1124_INT_PORT, SJA1124_INT_PIN) == false)
				{
					/* Check if the slave response frame is received. */
					if(Lin_GetStatus(channel, &linFrame_Slave_Response, &linStatus) == LIN_RX_OK)
					{
					   frameSent = true;
					}
					/* Check if frame error is detected. */
					else if(linStatus.les != 0)
					{
					   frameSentError[channel] = true;
					}
				}
			}

		}
	}
}


/********************************************************************
 * This function read the ID of SJA1124.
 *******************************************************************/
Std_ReturnType SJA1124_LIN_Read_Id(void)
{
	Std_ReturnType error = E_OK;
	uint8_t rcvBufferTemp[1];
	error = SJA1124_RWRegister(0, true, SJA1124_ID_ADDR, 1 , NULL, rcvBufferTemp);

	if(SJA1124_ID == rcvBufferTemp[0])
	    LOGI(TAG,"SJA1124 Detected. Read SJA1124 ID = %d.\r\n",rcvBufferTemp[0]);
	else
		LOGI(TAG,"No SJA1124 Detected. Read SJA1124 ID = %d.\r\n",rcvBufferTemp[0]);
	return error;
}
