/**
  ******************************************************************************
  * @file    kpm32xx_ddl_sci_spi.c
  * @author  Kiwi Software Team
  * @brief   SCI SPI DDL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of SPI peripheral:
  *          + Initialization and Configuration functions
  *          + Data transmit/receive with polling mode
  *          + Data transmit/receive with interrupt mode
  *          + Data transmit/receive with DMA mode
  * @note
  *          V1.0.0, 2022/08/03.
  *
  * Copyright (c) 2024, Kiwi Instruments Co,. Ltd.
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *
  *   2. 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.
  *
  *   3. 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.
  ******************************************************************************
  */

#include "kpm32xx_ddl.h"





#ifdef DDL_SCISPI_MODULE_ENABLED


/**
  * @brief  Enable SCI SPI TX or RX interrupt.
  *
  * @param  SCIx Pointer to SCI SPI instance.
  * @param  intrMask SCI SPI TX and RX mask.
  *
  * @retval None
  */
void DDL_SCISPI_IntEnable(SCI_Type *SCIx, uint32_t intrMask)
{
	SET_BIT(SCIx->IE, intrMask);
}


/**
  * @brief  Disable SCI SPI TX or RX interrupt.
  *
  * @param  SCIx Pointer to SCI SPI instance.
  * @param  intrMask SCI SPI TX and RX mask.
  *
  * @retval None
  */
void DDL_SCISPI_IntDisable(SCI_Type *SCIx, uint32_t intrMask)
{
	CLEAR_BIT(SCIx->IE, intrMask);
}


/**
  * @brief  Configure the elements of structure SCISPI_Init_T to default values.
  *         where, for example:(Fclk 96M)
  *         Baud        clkPres         brDiv1And2
  *         1000000     1               0x17 << 16
  *         400000      2               0x1D << 16
  *         100000      3               0x3B << 16
  *         10000       4               0x12B << 16
  * @param  pSCISPIStruct Pointer to a SCISPI_Init_T structure that contains
  *                  the configuration information for the given SCI SPI module.
  *
  * @retval None
  */
void DDL_SCISPI_StructInit(SCISPI_Init_T *pSCISPIStruct)
{
    pSCISPIStruct->workMode  = SCISPI_MASTER;
	pSCISPIStruct->mlsb      = SCIUART_MSB_FIRST;
	pSCISPIStruct->dataFlt   = 0;
    pSCISPIStruct->clkPhase  = CLK_NOR;
    pSCISPIStruct->datPhase  = DATA_NOR;
    
	pSCISPIStruct->clkPres     = 2;
	pSCISPIStruct->brDiv1And2  = 29 << 16;
}


/**
  * @brief  Initializes the SCI SPI by the specified parameters
  *         in the SCISPI_Init_T and initialize the associated handle.
  * @param  SCIx     Pointer to SCI SPI instance.
  * @param  pSCISPIStruct   pointer to a SCISPI_Init_T structure that contains
  *                  the configuration information for the specified SPI.
  *
  * @retval None
  */
void DDL_SCISPI_Init(SCI_Type *SCIx, SCISPI_Init_T *pSCISPIStruct)
{
	/* Disable all possible interrupts */
	__DDL_SCISPI_INTRDISABLE(SCIx, 0xFFFF);    

	/* Set the SCI SPI Communication parameters */
    WRITE_REG(SCIx->CLK, pSCISPIStruct->clkPres);
	WRITE_REG(SCIx->BDR, pSCISPIStruct->brDiv1And2);
	
	/* Configure SPI as SPI mode*/
	__DDL_SCI_CONFIGURE_MODE(SCIx, SCI_AS_SPI_MODE);

	if (pSCISPIStruct->workMode == SCISPI_MASTER)
	{
		__DDL_SCISPI_AS_MASTER(SCIx);
	}
	else
	{
		__DDL_SCISPI_AS_SLAVE(SCIx);
	}

	/* Clock and Data Setting */
    if (pSCISPIStruct->datPhase == DATA_NOR)
	{
		__DDL_SCISPI_DATPHS_NORMAL(SCIx);
	}
	else /* Clock Normal and Data Inversion */
	{
		__DDL_SCISPI_DATPHS_INVERS(SCIx);
	}

	if (pSCISPIStruct->clkPhase == CLK_NOR)
	{
		__DDL_SCISPI_CLKPHS_NORMAL(SCIx);
	}
	else
	{
		__DDL_SCISPI_CLKPHS_INVERS(SCIx);
	}

	__DDL_SCI_MLSB_FIRST(SCIx, pSCISPIStruct->mlsb);
	
    /* Enable SPI Data Filter for MOSI (Slave Mode) and MISO (Master Mode) */
	if (pSCISPIStruct->dataFlt)
	{
		__DDL_SCI_INF_DATFILTER_ENABLE(SCIx);
	}

	if (pSCISPIStruct->clkFlt)
	{
		__DDL_SCI_INF_CLKFILTER_ENABLE(SCIx);
	}

	/* init the low level hardware : GPIO, CLOCK */

	if (pSCISPIStruct->workMode == SCISPI_MASTER)
	{
		/* SPI CS enable */
		__DDL_SCISPI_CSPIN_ENABLE(SCIx);
	}
}


/**
  * @brief  SCI SPI as master or slave transmits an amount of data through polling mode.
  * @param  SCIx     Pointer to SCI SPI instance.
  *         pData    pointer to data buffer.
  *         size     the amount of data to be sent.
  *         timeout  timeout duration.
  *
  * @retval None
  */
DDL_Status_T DDL_SCISPI_Transmit(SCI_Type *SCIx, uint8_t *pData, uint16_t size, uint32_t timeout)
{
    ASSERT_PARAM(pData != NULL);
	ASSERT_PARAM(size  != 0U);
	ASSERT_PARAM(timeout != 0);
    
	uint32_t  tickStart = 0;

	/* No error will happen in SCI SPI in Single-Transcation mode */
    uint32_t  errFlags = SCI_ST_FRMERR | SCI_ST_PRTERR | SCI_ST_OVRERR;

	/* Set as Tx mode */
    __DDL_SCISPI_SET_TX_MODE(SCIx);
    __DDL_SCISPI_CLEAR_RX_MODE(SCIx);

	/* CS Pin is valid only in master mode */
	if ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER)
	{
		if (size == 1)
		{
			__DDL_SCISPI_CSPIN_DEACTIVE(SCIx);
		}
		else
		{
			/* Make SCI SPI CS pin active */
			__DDL_SCISPI_CSPIN_ACTIVE(SCIx);
		}
		
	}
	/* init tickStart for timeout management */
	tickStart = DDL_GetTick();

	/* Wait till timeout happens or all data are sent-out */
	while (size > 0U)
	{
		/* data-trascation */
		SCIx->TXD = *pData;
		pData ++;
		size--;

		/* No error will happen in SPI Tx in Single-Transcation mode */
		if (SCIx->ST & errFlags)
		{
            WRITE_REG(SCIx->CLR, errFlags);
			if ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER)
			{
				/* Put CS pin to High. This causes CS from Low to High, forcing data-transaction termination */
				__DDL_SCISPI_CSPIN_DEACTIVE(SCIx);
			}
			return DDL_ERROR;
		}

		/* Wait for transaction completes */
		while(!(SCIx->ST & SCI_ST_TXEND))
		{
			if (DDL_GetTick() - tickStart >= timeout)
			{
				return DDL_TIMEOUT;
			}
		}
		/* Then clear the flag */
		WRITE_REG(SCIx->CLR, SCI_CLR_TXEND);
		/*Then start the next data frame traction */

		if ((size == 1) && ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER))
		{
			/*So that, CS will be pulled high when the last byte was sent-out*/
			__DDL_SCISPI_CSPIN_DEACTIVE(SCIx);
		}
	}
	/* should check error to set error code*/
    return DDL_OK;
}


/**
  * @brief  SCI SPI as master or slave (Half-Duplex) receives an amount of data through polling mode.
  * @param  SCIx     Pointer to SCI SPI instance.
  * @param  pData    pointer to data buffer.
  * @param  size     the amount of data to be sent.
  * @param  timeout  timeout duration.
  *
  * @retval DDL status
  */
DDL_Status_T DDL_SCISPI_Receive(SCI_Type *SCIx, uint8_t *pData, uint16_t size, uint32_t timeout)
{
    ASSERT_PARAM(pData != NULL);
	ASSERT_PARAM(size  != 0U);
	ASSERT_PARAM(timeout != 0);
	uint32_t tickStart = 0;
	/* Error flags */
	uint32_t errFlags = SCI_ST_FRMERR | SCI_ST_PRTERR | SCI_ST_OVRERR;

	/* Set as Rx mode */
    __DDL_SCISPI_SET_RX_MODE(SCIx);
    __DDL_SCISPI_CLEAR_TX_MODE(SCIx);

	if ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER)
	{
		/* Make SCI SPI CS pin active */
		__DDL_SCISPI_CSPIN_ACTIVE(SCIx);
	}

	/* init tickStart for timeout management */
	tickStart = DDL_GetTick();

	/* as long as data have to be received */
	while (size > 0U)
	{
		if ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER)
		{
			/* Write dummy data to read data */
			SCIx->TXD = __SPI_08BIT_DUMMY_DATA__;
			while(!(SCIx->ST & SCI_ST_RXEND))
			{
				if (DDL_GetTick() - tickStart >= timeout)
				{
					return DDL_TIMEOUT;
				}
			}
			WRITE_REG(SCIx->CLR, SCI_CLR_RXEND);

			/* Should check the errors while SPI stay in receive mode */
			if (SCIx->ST & errFlags)
			{
				if ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER)
				{
					/* Put CS pin to High. This causes CS from Low to High, forcing data-transaction termination */
					__DDL_SCISPI_CSPIN_DEACTIVE(SCIx);
				}
				return DDL_ERROR;
			}
		}

		if ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER)
		{
			*pData = SCIx->RXD;
			pData ++;
			size --;
		}
		else
		{
            while(!(SCIx->ST & SCI_ST_RXEND))
            {
                if (DDL_GetTick() - tickStart >= timeout)
                {
                    return DDL_TIMEOUT;
                }
            }
            WRITE_REG(SCIx->CLR, SCI_CLR_RXEND);
			*pData = SCIx->RXD;
			pData ++;
			size--;
		}

		if (size == 1)
		{
			if ((READ_BIT(SCIx->MSPI, 0x04)>>2) == SCISPI_MASTER)
			{
				__DDL_SCISPI_CSPIN_DEACTIVE(SCIx);
			}
		}
	}

	/* At end of Rx process, restore hscispi->rxState to Ready */
    return DDL_OK;
}

#endif /* DDL_SCISPI_MODULE_ENABLED */

