/**
  ******************************************************************************
  * @file    kpm32xx_ddl_uart.c
  * @author  Kiwi Software Team
  * @brief   SCI UART DDL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of SCI UART:
  *          + Module initialization
  *          + Transmit and receive data with polling mode
  *          + Transmit and receive data with interrupt mode
  *          + Transmit and receive data with 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_SCIUART_MODULE_ENABLED

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


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

/**
  * @brief  Write one frame of data into SCI UART.
  * @param  SCIx  SCI instance.
  *
  * @param  data  the value to be writen into SCI UART module.
  *
  * @retval none.
  */
void DDL_SCIUART_WriteData(SCI_Type *SCIx, uint16_t data)
{
    if (((SCIx->MUART&0x03) == 0x02)&&((SCIx->MUART&0x0C) == 0x00))
    {
        MODIFY_REG(SCIx->MUART, (1<<10), ((data>>7)<<10));
    }
    
    /* Send out this character */
    SCIx->TXD = data;
}

/**
  * @brief  Get one byte from SCI UART.
  * @param  SCIx  SCI instance.
  *
  * @retval uint16_t type.
  */
uint16_t DDL_SCIUART_GetData(SCI_Type *SCIx)
{
	uint16_t data;

	data = (uint16_t)(SCIx->RXD);
    if (((SCIx->MUART&0x03) == 0x02)&&((SCIx->MUART&0x0C) == 0x00))
    {
        data |= (READ_BIT(SCIx->MUART, (1<<16)))>>9;
    }
    
    return data;
}


/**
  * @brief  Put one byte into SCI UART.
  * @param  SCIx  SCI instance.
  * @param  ch    the value to be put into SCI UART module.
  *
  * @retval none
  */
void DDL_SCIUART_WaitPutCharCmplt(SCI_Type *SCIx, uint8_t ch)
{
    if (((SCIx->MUART&0x03) == 0x02)&&((SCIx->MUART&0x0C) == 0x00))
    {
        MODIFY_REG(SCIx->MUART, (1<<10), ((ch>>7)<<10));
    }
    /* Send out this character */
    SCIx->TXD = ch;

	while(!(SCIx->ST & SCI_ST_TXEND));
	/* Then clear the flag */
	WRITE_REG(SCIx->CLR, SCI_CLR_TXEND);
}


/**
  * @brief  Get one byte from SCI UART.
  * @param  SCIx  SCI instance.
  *
  * @retval uint16_t type.
  */
uint16_t DDL_SCIUART_WaitGetCharCmplt(SCI_Type *SCIx)
{
	uint16_t data;

	/* Wait till data-ready */
	while(!(SCIx->ST & SCI_ST_RXEND));
	data = (uint16_t)(SCIx->RXD);
    if (((SCIx->MUART&0x03) == 0x02)&&((SCIx->MUART&0x0C) == 0x00))
    {
        data |= (READ_BIT(SCIx->MUART, (1<<16)))>>9;
    }
	WRITE_REG(SCIx->CLR, SCI_CLR_RXEND);

    return data;
}


/**
  * @brief  Configure the elements of structure SCIUART_Init_T to default values.
  *         where, for example:(Fclk 100M)
  *         Baud        clkPres         brDiv1And2
  *         1200        7               0xA1 << 16
  *         2400        6               0xA1 << 16
  *         4800        5               0xA1 << 16
  *         9600        4               0xA1 << 16
  *         19200       3               0xA1 << 16
  *         38400       2               0xA1 << 16
  *         57600       1               0xD8 << 16
  *         115200      0               0xD8 << 16
  * @param  pSCIUartStruct Pointer to a SCIUART_Init_T structure that contains
  *                  the configuration information for the given SCI UART module.
  *
  * @retval None
  */
void DDL_SCIUART_StructInit(SCIUART_Init_T *pSCIUartStruct)
{
	pSCIUartStruct->mlsb        = SCIUART_LSB_FIRST;
    pSCIUartStruct->parity      = SCIUART_PARITY_EVEN;
    pSCIUartStruct->stopBits    = SCIUART_STOPBITS_1;
    pSCIUartStruct->wordLength  = SCIUART_WORDLENGTH_8B;
	pSCIUartStruct->dataInv     = 0;
	pSCIUartStruct->dataFlt     = 0;
	pSCIUartStruct->hwFlwCtrl   = SCIUART_HWCTRL_NONE;
	pSCIUartStruct->clkPres     = 3;
	pSCIUartStruct->brDiv1And2  = 0x144 << 16;
}


/**
  * @brief  Initializes the SCI UART by the specified parameters in the SCIUART_Init_T.
  *
  * @param  SCIx Pointer to SCI UART instance.
  * @param  pSCIUartStruct Pointer to a SCIUART_Init_T structure that contains
  *                  the configuration information for the given SCI UART module.
  *
  * @retval DDL status
  */
DDL_Status_T DDL_SCIUART_Init(SCI_Type *SCIx, SCIUART_Init_T *pSCIUartStruct)
{
	uint32_t tmpValue = 0;

	/* Check the parameters */
	ASSERT_PARAM(IS_SCIUART_WORD_LENGTH(pSCIUartStruct->wordLength));
	ASSERT_PARAM(IS_SCIUART_STOPBITS(pSCIUartStruct->stopBits));
	ASSERT_PARAM(IS_SCIUART_PARITY(pSCIUartStruct->parity));

	if ((pSCIUartStruct->wordLength == SCIUART_WORDLENGTH_9B) && (pSCIUartStruct->parity != SCIUART_PARITY_NONE))
	{
	    return DDL_ERROR;
	}

	/* Configure SCI as UART mode */
	__DDL_SCI_CONFIGURE_MODE(SCIx, SCI_AS_UART_MODE);

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

	/* Enable UART RX Filter */
	if (pSCIUartStruct->dataFlt)
	{
		__DDL_SCI_INF_DATFILTER_ENABLE(SCIx);
	}

	/* hardware flow control */
	tmpValue |= pSCIUartStruct->hwFlwCtrl;

	if (pSCIUartStruct->dataInv)
	{
		__DDL_SCI_DATA_INVERSION(SCIx);
	}

	/* Configure Word-Length, stopBits, parity */
	
    if ((pSCIUartStruct->wordLength == SCIUART_WORDLENGTH_8B)&&(pSCIUartStruct->parity == SCIUART_PARITY_NONE))
    {
        pSCIUartStruct->wordLength = (((uint32_t)0x00000002U) << SCI_MUART_DATLEN_Pos);
    }
    tmpValue |= pSCIUartStruct->stopBits | pSCIUartStruct->wordLength | pSCIUartStruct->parity;
	WRITE_REG(SCIx->MUART, tmpValue);

	/* MSB or LSB Configuration */
	__DDL_SCI_MLSB_FIRST(SCIx, pSCIUartStruct->mlsb);
    
    
    /* Till now, UART parameters have already be configured */
    if (((SCIx->MUART&0x03) == 0x02)&&((SCIx->MUART&0x0C) == 0x00))
    {
        SCIx->MUART |= (1<<8);
    }

	return DDL_OK;
}


/**
  * @brief  Transmits an amount of data through polling mode.
  *
  * @param  SCIx Pointer to SCI UART instance.
  * @param  pData    pointer to data buffer.
  * @param  size     the amount of data frame to be sent.
  * @param  timeout  timeout duration.
  *
  * @retval DDL status
  */
DDL_Status_T DDL_SCIUART_Transmit(SCI_Type *SCIx, uint8_t *pData, uint16_t size, uint32_t timeout)
{
	ASSERT_PARAM(pData != NULL);
	ASSERT_PARAM(size  != 0U);
	ASSERT_PARAM(timeout != DDL_MAX_DELAY);
	ASSERT_PARAM(timeout != 0);

	uint32_t  tickStart = 0;
    uint32_t  errFlags  = SCI_ST_FRMERR | SCI_ST_PRTERR | SCI_ST_OVRERR;

	__DDL_SCIUART_TX_ENABLE(SCIx);

	tickStart = DDL_GetTick();

	/* Wait till timeout happens or all data are sent-out */
	while (size > 0U)
	{
        if (((SCIx->MUART&0x03) == 0x02)&&((SCIx->MUART&0x0C) == 0x00))
        {
            MODIFY_REG(SCIx->MUART, (1<<10), (((*pData & 0xFFU)>>7)<<10));
        }
    
        /* Send out this character */
        SCIx->TXD = *pData & 0xFFU;
//		SCIx->TXD = *pData & 0xFFU;
		pData ++;
		size--;

		/* Check errs while transmitting */
		if (SCIx->ST & errFlags)
		{
			WRITE_REG(SCIx->CLR, errFlags);
			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);
	}

	return DDL_OK;
}


/**
  * @brief  Receives an amount of data through polling mode.
  *
  * @param  SCIx     SCIx Pointer to SCI UART instance.
  * @param  pData    pointer to data buffer.
  * @param  size     the amount of data frame to receive.
  * @param  timeout  timeout duration.
  *
  * @retval DDL status
  */
DDL_Status_T DDL_SCIUART_Receive(SCI_Type *SCIx, uint8_t *pData, uint16_t size, uint32_t timeout)
{
	ASSERT_PARAM(pData != NULL);
	ASSERT_PARAM(size  != 0U);
	ASSERT_PARAM(timeout != DDL_MAX_DELAY);
	ASSERT_PARAM(timeout != 0);

	uint32_t tickStart = 0;
	uint32_t errFlags  = SCI_ST_FRMERR | SCI_ST_PRTERR | SCI_ST_OVRERR;

	__DDL_SCIUART_RX_ENABLE(SCIx);

	tickStart = DDL_GetTick();

	while (size > 0U)
	{
		/* 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);

		*pData = (uint8_t)(SCIx->RXD);
		pData++;

		if (SCIx->ST & errFlags)
		{
			WRITE_REG(SCIx->CLR, errFlags);
			return DDL_ERROR;
		}

		size--;
	}

	return DDL_OK;
}

#endif /* DDL_SCIUART_MODULE_ENABLED */

