/**
  ******************************************************************************
  * @file    kpm32xx_ddl_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_I2C_MODULE_ENABLED

/**
  * @brief  Enable IIC interrupt.
  *
  * @param  I2Cx Pointer to IIC instance.
  * @param  intrMask IIC mask.
  *
  * @retval None
  */
void DDL_IIC_IntEnable(I2C_Type *I2Cx, uint32_t intrMask)
{
	SET_BIT(I2Cx->INTEN, intrMask);
}


/**
  * @brief  Disable IIC interrupt.
  *
  * @param  I2Cx Pointer to IIC instance.
  * @param  intrMask IIC mask.
  *
  * @retval None
  */
void DDL_IIC_IntDisable(I2C_Type *I2Cx, uint32_t intrMask)
{
	CLEAR_BIT(I2Cx->INTEN, intrMask);
}


/**
  * @brief  Configure the elements of structure I2C_Init_T to default values.
  *         where, for example:(Fclk 200M)
  *         Baud        prescaler	widthHigh	widthLow
  *         1000000     1           17  	    17
  *         400000      1           53          53
  *         100000      1           240         241
  *         10000       20          233         233
  * @param  pI2CStruct Pointer to a I2C_Init_T structure that contains
  *                  the configuration information for the given I2C module.
  *
  * @retval None
  */
void DDL_I2C_StructInit(I2C_Init_T *pI2CStruct)
{
    pI2CStruct->addrMode = I2C_ADDR_07BITS;
	pI2CStruct->workMode = I2C_MASTER_MODE;
    pI2CStruct->i2cFltEnable = 0;
	pI2CStruct->wakupEnable = 0;
	pI2CStruct->prescaler = 1;
	pI2CStruct->widthHigh = 53;
	pI2CStruct->widthLow = 53;
}


/**
  * @brief  Initializes the I2C by the specified parameters
  *         in the I2C_Init_T and initialize the associated handle.
  * @param  I2Cx     Pointer to IIC instance.
  * @param  pI2CStruct   pointer to a I2C_Init_T structure that contains
  *                the configuration information for the specified I2C.
  *
  * @retval None
  */
void DDL_I2C_Init(I2C_Type *I2Cx, I2C_Init_T *pI2CStruct)
{
	/* Disable all possible interrupts:
	 * Interrupt rises on 8th or 9th Clock and when STOP signal is detected.
	 */
	__DDL_I2C_INTDISABLE(I2Cx, I2C_INTEN_STOPINT | I2C_INTEN_WTIMEN | I2C_INTEN_ADDRINT);
	__DDL_I2C_7BIT_ADMOD_SET(I2Cx);
	
	/* Set the I2C Communication parameters */
    I2Cx->DIV = pI2CStruct->prescaler;
    __DDL_I2C_HIGHANDLOW_LVL_CONFIG(I2Cx, pI2CStruct->widthHigh, pI2CStruct->widthLow);
    if (pI2CStruct->i2cFltEnable)
	{
		/* Configure I2C Filter Enable */
		__DDL_I2C_FILTER_ON(I2Cx);
	}

	/* Enable I2C Module */
	__DDL_I2C_ENABLE(I2Cx);

}


/**
  * @brief  I2C as master transmits an amount of data in polling mode.
  * @param  SCIx Pointer to IIC instance.
  *         slvAddr address to be sent.
  *         pData   pointer to data buffer.
  *         size    the amount of data to be sent.
  *         timeout specify the max wait time.
  * @retval DDL status
  */
DDL_Status_T DDL_I2C_Master_Transmit(I2C_Type *I2Cx, uint16_t slvAddr, uint8_t *pData, uint16_t size, uint32_t timeout)
{
	uint16_t datacount;
	uint8_t dataflag;
	uint32_t i = 0;
	uint32_t  tickStart = 0;

	__DDL_I2C_WRITE_CONFIG(I2Cx);
	I2Cx->DATACNT = size;
	I2Cx->FIFOCTL = 2;
	/* Disable all possible interrupts:
	 * Interrupt rises on 8th or 9th Clock and when STOP signal is detected.
	 */
	__DDL_I2C_WTIM_8OR9THCLK_CONFIG(I2Cx, I2C_INTOCUR_ON9TH_CLK);
	__DDL_I2C_CLEAR_ADDR_INTR(I2Cx);
	__DDL_I2C_CLEAR_DATA_INTR(I2Cx);
		
	__DDL_I2C_CLEAR_STOP_INTR(I2Cx);
	__DDL_I2C_INTDISABLE(I2Cx, I2C_INTEN_STOPINT | I2C_INTEN_WTIMEN | I2C_INTEN_ADDRINT | I2C_INTEN_FIFOTHRE | I2C_INTEN_FIFOFULL | I2C_INTEN_FIFOEMPTY | I2C_INTEN_TCNTEN);

	/* init tickStart for timeout management */
	tickStart = DDL_GetTick();
	/*First, send out I2C slave address*/
	I2Cx->SLAD = slvAddr;
	/* Generate One I2C Start-up Signal*/
	__DDL_I2C_STT_GENERATE(I2Cx);

	/* ACK Occurs when One-Data frame transmission completes.
	 * And it's auto-cleared when a new Data frame transmission starts.
	 */
	while((!I2Cx->INTSR & I2C_INTSR_ADDR))
	{
		if (DDL_GetTick() - tickStart >= timeout)
		{
			return DDL_TIMEOUT;
		}
	}
	/* data-transaction */
	while (size > 0)
	{
		if (I2Cx->DATACNT >= 8)
		{
			if (dataflag == 0)
			{
				for (i = 0;i < 8;i++)
				{
					I2Cx->DT = *pData;
					pData ++;
					size--;
				}
				dataflag = 1;
				/* Open the fifothre Interrupt Ctrl */
				__DDL_I2C_WAIT_QUIT_SET(I2Cx);
			}
			datacount = size;
			
			if (I2Cx->FIFOST & I2C_FIFOST_FIFOTHR)
			{
				if (size >= 6)
				{
					for (i = 0;i < 6;i++)
					{
						I2Cx->DT = *pData;
						pData ++;
						size--;
					}
				}
				else
				{
					for (i = 0;i < datacount;i++)
					{
						I2Cx->DT = *pData;
						pData ++;
						size--;
					}
				}
			}
		}
		else
		{
			for (i = 0;i < I2Cx->DATACNT;i++)
			{
				I2Cx->DT = *pData;
				pData ++;
                size--;
			}
			__DDL_I2C_WAIT_QUIT_SET(I2Cx);
		}
		while((!(I2Cx->FIFOST & 0x100)))
		{
			if (DDL_GetTick() - tickStart >= timeout)
			{
				return DDL_TIMEOUT;
			}
		}
	}
	
	while((!(I2Cx->INTSR & I2C_INTSR_TCNTDONE)))
	{
		if (DDL_GetTick() - tickStart >= timeout)
		{
			return DDL_TIMEOUT;
		}
	}
	__DDL_I2C_CLEAR_TCNTDONE_INTR(I2Cx);
	/* Generate One I2C Stop Signal*/
	__DDL_I2C_STP_GENERATE(I2Cx);
	__DDL_I2C_CLEAR_ADDR_INTR(I2Cx);
	__DDL_I2C_CLEAR_DATA_INTR(I2Cx);
	__DDL_I2C_CLEAR_STOP_INTR(I2Cx);

	return DDL_OK;
}


/**
  * @brief  I2C as master receive an amount of data with polling.
  * @param  SCIx Pointer to IIC instance.
  *         slvAddr address to be sent.
  *         pData  pointer to data buffer.
  *         size   the amount of data to be sent.
  *         timeout specify the max wait time.
  * @retval DDL status
  */
DDL_Status_T DDL_I2C_Master_Receive(I2C_Type *I2Cx, uint16_t slvAddr, uint8_t *pData, uint16_t size, uint32_t timeout)
{
	uint16_t datacount;
	uint32_t tickStart = 0;
	uint8_t masterflag = 0;
	uint8_t i = 0;

	__DDL_I2C_READ_CONFIG(I2Cx);
	I2Cx->DATACNT = size;
	I2Cx->FIFOCTL = 6;
	/* Disable all possible interrupts:
	 * Interrupt rises on 8th or 9th Clock and when STOP signal is detected.
	 */
	__DDL_I2C_WTIM_8OR9THCLK_CONFIG(I2Cx, I2C_INTOCUR_ON9TH_CLK);
	__DDL_I2C_CLEAR_ADDR_INTR(I2Cx);
	__DDL_I2C_CLEAR_DATA_INTR(I2Cx);
	
	__DDL_I2C_CLEAR_STOP_INTR(I2Cx);
	__DDL_I2C_INTDISABLE(I2Cx, I2C_INTEN_STOPINT | I2C_INTEN_WTIMEN | I2C_INTEN_ADDRINT | I2C_INTEN_FIFOTHRE | I2C_INTEN_FIFOFULL | I2C_INTEN_FIFOEMPTY | I2C_INTEN_TCNTEN);

	/* init tickStart for timeout management */
	tickStart = DDL_GetTick();
	
	I2Cx->SLAD = slvAddr;
	__DDL_I2C_STT_GENERATE(I2Cx);
    
    while((!(I2Cx->INTSR & I2C_INTSR_ADDR)))
    {
        __DDL_I2C_CLEAR_ADDR_INTR(I2Cx);
    	if (DDL_GetTick() - tickStart >= timeout)
    	{
    		return DDL_TIMEOUT;
    	}
    }
    __DDL_I2C_CLEAR_ADDR_INTR(I2Cx);
    __DDL_I2C_ACK_GENERATE(I2Cx);
    __DDL_I2C_WAIT_QUIT_SET(I2Cx);
    __DDL_I2C_CLEAR_ADDR_INTR(I2Cx);

	/* as long as data have to be received */
	while (size > 0U)
	{
		/* data count less than 8*/
		if (I2Cx->INTSR & I2C_INTSR_TCNTDONE)
		{
			if (!masterflag)
			{
				masterflag = 1;
				__DDL_I2C_WTIM_8OR9THCLK_CONFIG(I2Cx, I2C_INTOCUR_ON9TH_CLK);
				__DDL_I2C_NACK_GENERATE(I2Cx);
				__DDL_I2C_CLEAR_TCNTDONE_INTR(I2Cx);
				__DDL_I2C_WAIT_QUIT_SET(I2Cx);
			}
			if ((I2Cx->DATACNT&0xFFFF) < 8)
			{
				for (i = 0;i < size; i++)
				{
					/* Start first Data-Frame Transaction */
					*pData = I2Cx->DT;
					pData ++;
				}
				size = 0;
			}
			else
			{
				for (i = 0;i < size; i++)
				{
					/* Start first Data-Frame Transaction */
					*pData = I2Cx->DT;
					pData ++;
					datacount++;
				}
				size = size-datacount;
			}
		}
		if ((I2Cx->DATACNT&0xFFFF) > 8)
		{
			if (I2Cx->FIFOST & 0x400)
			{
				for (i = 0;i < 6; i++)
				{
					/* Start first Data-Frame Transaction */
					*pData = I2Cx->DT;
					pData ++;
					datacount++;
					size--;
				}
			}
		}
		
	}
	
	while((!(I2Cx->INTSR & I2C_INTSR_TCNTDONE)))
	{
		if (DDL_GetTick() - tickStart >= timeout)
		{
			return DDL_TIMEOUT;
		}
	}
	/* Generate One I2C Stop Signal*/
    __DDL_I2C_CLEAR_FIFO(I2Cx);
	__DDL_I2C_STP_GENERATE(I2Cx);
	__DDL_I2C_CLEAR_ADDR_INTR(I2Cx);
	__DDL_I2C_CLEAR_DATA_INTR(I2Cx);
	__DDL_I2C_CLEAR_TCNTDONE_INTR(I2Cx);
	__DDL_I2C_CLEAR_STOP_INTR(I2Cx);
    __DDL_I2C_WAIT_QUIT_SET(I2Cx);

	return DDL_OK;
}




#endif /* DDL_I2C_MODULE_ENABLED */

