/**
  ******************************************************************************
  * @file    kpm32xx_ddl_sci_i2c.c
  * @author  Kiwi Software Team
  * @brief   I2C DDL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the I2C peripheral:
  *          1.peripheral initializes
  *          2.data transfer in polling mode/interrupt mode/DMA mode
  * @note
  *          V1.0.0, 2024/12/20.
  *
  * 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_SCII2C_MODULE_ENABLED
/**
  * @brief  Enable SCI IIC TX or RX interrupt.
  *
  * @param  SCIx Pointer to SCI IIC instance.
  * @param  intrMask SCI IIC TX and RX mask.
  *
  * @retval None
  */
void DDL_SCIIIC_IntEnable(SCI_Type *SCIx, uint32_t intrMask)
{
	SET_BIT(SCIx->IE, intrMask);
}


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


/**
  * @brief  Configure the elements of structure SCII2C_Init_T to default values.
  *         where, for example:(Fclk 200M)
  *         Baud        clkPres         brDiv1And2
  *         1000000     1               0x17 << 16
  *         400000      2               0x1D << 16
  *         100000      3               0x3B << 16
  *         10000       4               0x12B << 16
  * @param  pSCII2CStruct Pointer to a SCII2C_Init_T structure that contains
  *                  the configuration information for the given SCI I2C module.
  *
  * @retval None
  */
void DDL_SCII2C_StructInit(SCII2C_Init_T *pSCII2CStruct)
{
    pSCII2CStruct->mlsb        = SCII2C_MSB_FIRST;
	pSCII2CStruct->sdaHold     = SCII2C_HLDCNT_1_4;
	pSCII2CStruct->fastMode    = SCII2C_FASTMODE_ENABLE;
	pSCII2CStruct->clkPres     = 2;
	pSCII2CStruct->brDiv1And2  = 0x1D << 16;
}


/**
  * @brief  Initializes the SCI I2C by the specified parameters
  *         in the SCII2C_Init_T and initialize the associated handle.
  * @param  SCIx     Pointer to SCI IIC instance.
  * @param  pSCII2CStruct   pointer to a SCII2C_Init_T structure that contains
  *                the configuration information for the specified I2C.
  *
  * @retval None
  */
void DDL_SCII2C_Init(SCI_Type *SCIx, SCII2C_Init_T *pSCII2CStruct)
{
    /* Configure SCI as I2C mode */
	__DDL_SCI_CONFIGURE_MODE(SCIx, SCI_AS_I2C_MODE);

    /* Configure SCI I2C Baudrate */
	WRITE_REG(SCIx->CLK, pSCII2CStruct->clkPres);
	WRITE_REG(SCIx->BDR, pSCII2CStruct->brDiv1And2);

	__DDL_SCI_MLSB_FIRST(SCIx, pSCII2CStruct->mlsb);
	__DDL_SCII2C_SETHLDCNT(SCIx, pSCII2CStruct->sdaHold);
    
	/* Always enable Clock Sync feature */
	__DDL_SCII2C_CLKSYNC(SCIx, SCII2C_CLKSYNC_ENABLE);
	__DDL_SCII2C_FASTMODE(SCIx, pSCII2CStruct->fastMode);
}


/**
  * @brief  SCI I2C as master transmits an amount of data through polling mode.
  * @param  SCIx     Pointer to SCI IIC instance.
  *         slvAddr  slave address.
  *         pData    pointer to data buffer.
  *         size     the amount of data to be sent.
  *         timeout  timeout duration.
  *
  * @retval DDL status
  */
DDL_Status_T DDL_SCII2C_Master_Transmit(SCI_Type *SCIx, uint16_t slvAddr, 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;
    uint8_t   addFlag = 0;

	/* SUCHN_ST_PRTERR means ACK error for I2C of SCI module */
    uint32_t  errFlags = SCI_ST_PRTERR | SCI_ST_OVRERR;

    addFlag = 1;

	/* This will both SET Tx and CLEAR Rx mode */
	__DDL_SCII2C_SET_TX_MODE(SCIx);
	__DDL_SCII2C_CLEAR_RX_MODE(SCIx);

	/* Generate a Start Signal */
	__DDL_SCII2C_STT_GENERATE(SCIx);

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

	/* Wait till timeout happens or all data are sent-out */
	while (size > 0U)
	{
		/* First, send I2C target slave address */
		if (addFlag)
		{
			SCIx->TXD = slvAddr;
			addFlag = 0;
		}
		else
		{
			/* data-transaction */
			SCIx->TXD = *pData;
			pData ++;
			size--;
		}

		/* ACK error (parity) may happen in CSI Tx in Single-Transaction mode */
		if (SCIx->ST & errFlags)
		{
            WRITE_REG(SCIx->CLR, errFlags);
		}

		/* 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 */
	}

	/* Generate a Stop Signal */
	__DDL_SCII2C_STP_GENERATE(SCIx);

	/* should check error to set error code*/
    return DDL_OK;
}


/**
  * @brief  SCI I2C as master receives an amount of data through polling mode.
  * @param  SCIx     Pointer to SCI IIC instance.
  *         slvAddr  slave address.
  *         pData    pointer to data buffer.
  *         size     the amount of data to receive.
  *         timeout  timeout duration.
  *
  * @retval DDL status
  */
DDL_Status_T DDL_SCII2C_Master_Receive(SCI_Type *SCIx, uint16_t slvAddr, uint8_t *pData, uint16_t size, uint32_t timeout)
{
    ASSERT_PARAM(pData != NULL);
	ASSERT_PARAM(size  != 0U);
	ASSERT_PARAM(timeout != 0);
    ASSERT_PARAM(SCIx != NULL);
	uint32_t tickStart = 0;
	/* Error flags */
	uint32_t errFlags = SCI_ST_PRTERR | SCI_ST_FRMERR;
	uint32_t tmp = 0;
    uint8_t addFlag = 0;

	/* Construct a 'START' condition to start i2c transaction  */
	__DDL_SCII2C_STT_GENERATE(SCIx);

	/* init tickStart for timeout management */
	tickStart = DDL_GetTick();
    addFlag = 1;
	size = size + addFlag;

	/* as long as data have to be received */
	while (size > 0U)
	{
		if (addFlag > 0)
		{
            /* This will both SET Tx and CLEAR Rx mode */
            __DDL_SCII2C_SET_TX_MODE(SCIx);
            __DDL_SCII2C_CLEAR_RX_MODE(SCIx);
            
			SCIx->TXD = slvAddr & 0xFFU;

			/* Wait for transaction completes */
			while(!(SCIx->ST & SCI_ST_TXEND))
			{
				if (DDL_GetTick() - tickStart >= timeout)
				{
					return DDL_TIMEOUT;
				}
			}
			WRITE_REG(SCIx->CLR, SCI_CLR_TXEND);
		}
		else
		{
            /* This will both SET Rx and CLEAR Tx mode */
            __DDL_SCII2C_SET_RX_MODE(SCIx);
            __DDL_SCII2C_CLEAR_TX_MODE(SCIx);
			SCIx->TXD = __I2C_08BIT_DUMMY_DATA__;

			if(size == 1)
			{
				__DDL_SCII2C_NACK_GENERATE(SCIx);
			}

			/* Wait for transaction completes */
			while(!(SCIx->ST & SCI_ST_RXEND))
			{
				if (DDL_GetTick() - tickStart >= timeout)
				{
					return DDL_TIMEOUT;
				}
			}
			/* Then clear the flag */
			WRITE_REG(SCIx->CLR, SCI_CLR_RXEND);
		}

		/* Should check the errors while iic stay in receive mode */
		if (SCIx->ST & errFlags)
		{
            WRITE_REG(SCIx->CLR, errFlags);
		}

		/* Read out valid data */
		if (addFlag == 0)
		{
			*pData = SCIx->RXD;
			pData ++;
		}
		else
		{
			tmp = SCIx->RXD;
			tmp ++;
			addFlag = 0;
		}

		size --;
	}

	/* Construct a 'STOP' condition to terminate i2c transaction  */
	__DDL_SCII2C_STP_GENERATE(SCIx);
	/* Restore to default value */
	__DDL_SCII2C_ACK_GENERATE(SCIx);
    
    return DDL_OK;
}

#endif /* DDL_I2C_MODULE_ENABLED */
