/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2022. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_uart.c
 *
 * @brief This file provides uart integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_uart_reg.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/* Uart controller map */
static const UART_Type *const s_uartCtrlMap[UART_INDEX_MAX] = {UART0, UART1};
/* Uart callback pointer array */
static DeviceCallback_Type s_uartCallback[UART_INDEX_MAX] = {0};

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
/*!
* @brief UART initialize
*
* @param[in] UARTx: UART type pointer, x value can be 0 to 1
*                         - UART0
*                         - UART1
* @param[in] config: uart setting type pointer which contains the configuration
*                         information for the specified UART.
* @return none
*/
void UART_Init(UART_Type *UARTx, const UART_ConfigType *config)
{
    UART_IndexType uartIndex = UART0_INDEX;

    DEVICE_ASSERT(IS_UART_PERIPH(UARTx));
    DEVICE_ASSERT(IS_UART_SAMPLE_COUNT(config->sampleCnt));
    DEVICE_ASSERT(IS_UART_WORD_LEN(config->dataBits));
    DEVICE_ASSERT(IS_UART_STOPBITS(config->stopBits));
    DEVICE_ASSERT(IS_UART_PARITY(config->parity));

    switch ((uint32_t)UARTx)
    {
    case UART0_BASE:
        uartIndex = UART0_INDEX;
        CKGEN_Enable(CLK_UART0, ENABLE);
        CKGEN_SoftReset(SRST_UART0, ENABLE);
        break;

    case UART1_BASE:
        uartIndex = UART1_INDEX;
        CKGEN_Enable(CLK_UART1, ENABLE);
        CKGEN_SoftReset(SRST_UART1, ENABLE);
        break;

    default:
        break;
    }

    s_uartCallback[uartIndex] = config->callBack;

    UART_SetDataBits(UARTx, config->dataBits);
    UART_SetParity(UARTx, config->parity);
    UART_SetBaudrate(UARTx, config->baudrate, config->sampleCnt);
    UART_SetStopBit(UARTx, config->stopBits);

    if (config->fifoByteEn)
    {
        UART_Set2ByteFIFO(UARTx, ENABLE);
    }
    else
    {
        UART_Set2ByteFIFO(UARTx, DISABLE);
    }

    UART_EnableTX(UARTx, ENABLE);
    UART_EnableRX(UARTx, ENABLE);
}

/*!
* @brief UART uninitialize
*
* @param[in] UARTx: UART type pointer, x value can be 0 to 1
*                         - UART0
*                         - UART1
* @return none
*/
void UART_DeInit(UART_Type *UARTx)
{
    DEVICE_ASSERT(IS_UART_PERIPH(UARTx));

    switch ((uint32_t)UARTx)
    {
    case UART0_BASE:
        NVIC_DisableIRQ(UART0_IRQn);
        NVIC_ClearPendingIRQ(UART0_IRQn);
        CKGEN_SoftReset(SRST_UART0, DISABLE);
        CKGEN_Enable(CLK_UART0, DISABLE);
        s_uartCallback[UART0_INDEX] = NULL;
        break;

    case UART1_BASE:
        NVIC_DisableIRQ(UART1_IRQn);
        NVIC_ClearPendingIRQ(UART1_IRQn);
        CKGEN_SoftReset(SRST_UART1, DISABLE);
        CKGEN_Enable(CLK_UART1, DISABLE);
        s_uartCallback[UART1_INDEX] = NULL;
        break;

    default:
        break;
    }
}

/*!
* @brief Set UART baudrate
*
* @param[in] UARTx: UART type pointer, x value can be 0 to 1
*                         - UART0
*                         - UART1
* @param[in] baudrate: UART baudrate
* @param[in] sampleCounter: Division factor for bus clock, value can be
*                         - UART_SMP_CNT0: baud_rate = APB CLOCK/16/{DLH, DLL}
*                         - UART_SMP_CNT1: baud_rate = APB CLOCK/8/{DLH, DLL}
*                         - UART_SMP_CNT2: baud_rate = APB CLOCK/4/{DLH, DLL}
*                         - UART_SMP_CNT3: baud_rate = APB CLOCK/16/{DLM, DLL}
* @return none
*/
void UART_SetBaudrate(UART_Type *UARTx, uint32_t baudrate, UART_SampleCntType sampleCounter)
{
    float divisor = 0;
    uint8_t div = 16;
    float fuart_busclk = 0;
    float fdiv = 0;
    float fbaudrate = 0;

    DEVICE_ASSERT(IS_UART_PERIPH(UARTx));
    DEVICE_ASSERT(IS_UART_SAMPLE_COUNT(sampleCounter));

    switch (sampleCounter)
    {
    case UART_SMP_CNT2:
        div = 4;
        break;

    case UART_SMP_CNT1:
        div = 8;
        break;

    default:
        div = 16;
        break;
    }

    DEVICE_ASSERT(IS_UART_BAUDRATE(baudrate, div));

    UART_SetSampleCounter(UARTx, sampleCounter);
    fuart_busclk = (float) (g_periphAPBClock);
    fdiv = (float) div;
    fbaudrate = (float) baudrate;
    divisor = fuart_busclk / fdiv / fbaudrate;
    UART_SetDivisor(UARTx, divisor);
    UART_SetGuard(UARTx, UART_GUARDCNT_DEF);
}

/*!
* @brief Get UART type pointer
*
* @param[in] uartIndex: UART index, value can be 0 to 2
*                         - UART0_INDEX
*                         - UART1_INDEX
* @return UART type pointer
*                         - UART0
*                         - UART1
*/
UART_Type *UART_GetUARTCtrl(UART_IndexType uartIndex)
{
    UART_Type *UARTx = 0;

    DEVICE_ASSERT(IS_UART_INDEX(uartIndex));

    if (uartIndex < UART_INDEX_MAX)
    {
        UARTx = (UART_Type *)(UART0_BASE + ((uint32_t)uartIndex << 12));
    }

    return UARTx;
}

/*!
* @brief UART send data by normal mode
*
* @param[in] UARTx: UART type pointer, x value can be 0 to 1
*                         - UART0
*                         - UART1
* @return Function return state:
*                         -SUCCESS
*                         -ERROR: timeout
*/
ERROR_Type UART_SendData(UART_Type *UARTx, uint16_t data)
{
    ERROR_Type ret = ERROR;
    uint32_t i = 0;

    DEVICE_ASSERT(IS_UART_PERIPH(UARTx));

    for (i = 0; i < 0xFFF; i++)
    {
        if (!UART_TxIsFull(UARTx))
        {
            UARTx->RBR = (data & (UART_RBR_RBR_THR_Msk));
            ret = SUCCESS;
            break;
        }
    }

    return ret;
}

/*!
* @brief UART receive data by normal mode
*
* @param[in] UARTx: UART type pointer, x value can be 0 to 1
*                         - UART0
*                         - UART1
* @return Get uart data
*/
uint16_t UART_ReceiveData(UART_Type *UARTx)
{
    DEVICE_ASSERT(IS_UART_PERIPH(UARTx));

    return (uint16_t)(UARTx->RBR & UART_RBR_RBR_THR_Msk);
}

/*!
* @brief Set UART Parity
*
* @param[in] UARTx: UART type pointer, x value can be 0 to 1
*                         - UART0
*                         - UART1
* @param[in] parity: UART parity set
*                         - UART_PARI_NO: no parity
*                         - UART_PARI_ODD: ODD parity
*                         - UART_PARI_EVEN: EVEN parity
* @return none
*/
void UART_SetParity(UART_Type *UARTx, UART_ParityType parity)
{
    UARTx->LCR0 &= ~UART_LCR0_SP_Msk;

    switch (parity)
    {
    case UART_PARI_ODD:                         /* UART_PARI_ODD */
        UARTx->LCR0 |= UART_LCR0_PEN_Msk;         /* parity enable */
        UARTx->LCR0 &= ~UART_LCR0_EPS_Msk;        /* set EPS,  0: odd  1: even*/
        break;

    case UART_PARI_EVEN:                        /* UART_PARI_EVEN */
        UARTx->LCR0 |= UART_LCR0_PEN_Msk;         /* parity enable */
        UARTx->LCR0 |= UART_LCR0_EPS_Msk;         /* set EPS,  0: odd  1: even*/
        break;

    case UART_PARI_NO:                          /* UART_PARI_NO */
        UARTx->LCR0 &= ~UART_LCR0_PEN_Msk;        /* parity disable */
        UARTx->LCR0 &= ~UART_LCR0_EPS_Msk;        /* set EPS,  0: odd  1: even*/
        break;

    default:
        UARTx->LCR0 &= ~UART_LCR0_PEN_Msk;        /* parity disable */
        UARTx->LCR0 &= ~UART_LCR0_EPS_Msk;        /* set EPS,  0: odd  1: even*/
        break;
    }
}

/*!
* @brief Set UART call back
*
* @param[in] UARTx: UART type pointer, x value can be 0 to 1
*                         - UART0
*                         - UART1
* @param[in] Callback: UART Callback function which will be called in UARTx_IRQHandler.
* @return none
*/
void UART_SetCallback(UART_Type *UARTx, const DeviceCallback_Type callback)
{
    uint8_t uartIndex = 0;

    DEVICE_ASSERT(IS_UART_PERIPH(UARTx));

    for (uartIndex = 0; uartIndex < (uint8_t)UART_INDEX_MAX; uartIndex++)
    {
        if (s_uartCtrlMap[uartIndex] == UARTx)
        {
            s_uartCallback[uartIndex] = callback;
            break;
        }
    }
}

/*!
* @brief UART Interrupt Handler
*
* @param[in] uartIndex: Uart index, value can be 0 to 2
* @return none
*/
static void UART_InterruptHandler(UART_Type *UARTx)
{
    UART_IndexType uartIndex = UART0_INDEX;

    uint32_t lsr0 = UARTx->LSR0;
    uint32_t lsr1 = UARTx->LSR1;

    UARTx->LSR0 = lsr0;
    UARTx->LSR1 = lsr1;

    switch ((uint32_t)UARTx)
    {
    case UART0_BASE:
        uartIndex = UART0_INDEX;
        break;

    case UART1_BASE:
        uartIndex = UART1_INDEX;
        break;

    default:
        break;
    }

    if (s_uartCallback[uartIndex])
    {
        s_uartCallback[uartIndex](UARTx, lsr0, lsr1);
    }
}

/*!
* @brief UART0 Interrupt Handler
*
* @param[in] none
* @return none
*/
void UART0_IRQHandler(void)
{
    UART_InterruptHandler(UART0);
}

/*!
* @brief UART1 Interrupt Handler
*
* @param[in] none
* @return none
*/
void UART1_IRQHandler(void)
{
    UART_InterruptHandler(UART1);
}

/* =============================================  EOF  ============================================== */
