/******************************************************************************
 * $Revision: 423 $
 * $Date:: 2017-04-07 16:03:30 +0900#$
 *****************************************************************************/
/* __DISCLAIMER_START__                                                      */
/******************************************************************************
* (c)2017, Cypress Semiconductor Corporation
* or a subsidiary of Cypress Semiconductor Corporation. All rights
* reserved.
*
* This software, including source code, documentation and related
* materials ( "Software" ), is owned by Cypress Semiconductor
* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
* and subject to worldwide patent protection (United States and foreign),
* United States copyright laws and international treaty provisions.
* Therefore, you may use this Software only as provided in the license
* agreement accompanying the software package from which you
* obtained this Software ( "EULA" ).
*
* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
* non-transferable license to copy, modify, and compile the
* Software source code solely for use in connection with Cypress' s
* integrated circuit products. Any reproduction, modification, translation,
* compilation, or representation of this Software except as specified
* above is prohibited without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. Cypress reserves the right to make
* changes to the Software without notice. Cypress does not assume any
* liability arising out of the application or use of the Software or any
* product or circuit described in the Software. Cypress does not
* authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ( "High Risk Product" ). By
* including Cypress' s product in a High Risk Product, the manufacturer
* of such system or application assumes all risk of such use and in doing
* so agrees to indemnify Cypress against all liability.
******************************************************************************/
/* __DISCLAIMER_END__                                                        */
/*****************************************************************************/
/** \file mfs_lin.c
 **
 ** A detailed description is available at
 ** @link LinGroup LIN Module description @endlink
 **
 ** History:
 **   - 2014-07-02  0.01  HS  Initial version for Traveo
 *****************************************************************************/

/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "pdl.h"
#include "interrupts.h"

#if defined(PDL_PERIPHERAL_LIN_ACTIVE)

/**
 *****************************************************************************
 ** \addtogroup LinGroup
 *****************************************************************************/
/** @{ */

/*****************************************************************************/
/* Local pre-processor symbols/macros ('#define')                            */
/*****************************************************************************/
#define LIN_MODE_SELECT_LIN     (0x3)
#define LIN_ESCR_MASK_LBL1_0    (0x3)
#define LIN_ESCR_SHIFT_LBL2     (2u)


/*****************************************************************************/
/* Global variable definitions (declared in header file with 'extern')       */
/*****************************************************************************/


/*****************************************************************************/
/* Local type definitions ('typedef')                                        */
/*****************************************************************************/

/** LIN internal data type */
typedef struct stc_lin_intern_data
{
    /** Status and Rx/Tx Error callback function */
    lin_status_cb_func_ptr_t   pfnStatusCbFunction;
    /** Received data */
    uint8_t au8ReceptionData[LIN_DATA_LENGTH_MAX];
    /** Received data length */
    uint8_t u8ReceivedDataLength;
}stc_lin_intern_data_t;


/*****************************************************************************/
/* Local variable definitions ('static')                                     */
/*****************************************************************************/


/*****************************************************************************/
/* Local function prototypes ('static')                                      */
/*****************************************************************************/
static stc_lin_intern_data_t* MfsLinGetInternDataPtr(pstc_lin_type_t pstcLin);
static void MfsLinIrqHandlerStatus(pstc_lin_type_t pstcLin,
                           stc_lin_intern_data_t *pstcLinInternData);
static void MfsLinFifoUseInit(pstc_lin_type_t pstcLin);

/*****************************************************************************/
/* Function implementation - global ('extern') and local ('static')          */
/*****************************************************************************/

#include "mfs_lin_generated.c"

/**
 *****************************************************************************
 ** \brief ISR callback for LIN status and error interrupt.
 **
 ** This handler is called by the LIN ISR whenever a LIN triggers
 ** status and error interrupt.
 ** It in return calls the callback function that has been given
 ** during LIN initialization (see Mfs_Lin_Init() and stc_lin_config_t) and clears
 ** the interrupt flags of the LIN.
 **
 ** \param [in] pstcFrt           LIN unit instance that caused the interrupt.
 ** \param [in] pstcLinInternData Internal data associated with the LIN instance.
 *****************************************************************************/
static void MfsLinIrqHandlerStatus(pstc_lin_type_t pstcLin,
                                stc_lin_intern_data_t *pstcLinInternData)
{
    un_lin_interrupt_trigger_t  unIntTrigger    = { 0 };
    un_lin_detected_error_t     unDetectedError = { 0 };
    uint8_t                     u8DataCnt;
    uint8_t                     u8Data;
    un_cpg_mfsn_lin_lamsrc_t   unLAMSRC = { 0 };
    un_cpg_mfsn_lin_lamsr_t    unLAMSR  = { 0 };
    un_cpg_mfsn_lin_lamesrc_t  unLAMESRC= { 0 };
    un_cpg_mfsn_lin_lamesr_t   unLAMESR = { 0 };
    un_cpg_mfsn_lin_ssr_t      unSSR    = { 0 };
    un_cpg_mfsn_lin_ssrs_t     unSSRS   = { 0 };
    un_cpg_mfsn_lin_ssrc_t     unSSRC   = { 0 };

    /* LIN break field is Detected ? */
    if(pstcLin->unSSR.stcField.u1LBD != 0)
    {
        /* Clear Flag */
        unSSRC.stcField.u1LBDC = 1;
        PDL_WRITE_REG_SYNC(pstcLin->unSSRC.u8Register, unSSRC.u8Register);

        /* Set flag */
        unIntTrigger.stcBits.DetectBreak = 1;
    }

    /* Read status register */
    unLAMSR.u8Register = pstcLin->unLAMSR.u8Register;
    /* Auto header reception is completed? */
    if(unLAMSR.stcField.u1LAHC != 0)
    {
        /* Clear Flag */
        unLAMSRC.stcField.u1LAHCC = 1;
        PDL_WRITE_REG_SYNC(pstcLin->unLAMSRC.u8Register, unLAMSRC.u8Register);

        /* Set flag */
        unIntTrigger.stcBits.CompleteAutoHeader = 1;
    }
    /* Checksum calculation is completed? */
    if(unLAMSR.stcField.u1LCSC != 0)
    {
        /* Clear Flag */
        unLAMSRC.stcField.u1LCSCC = 1;
        PDL_WRITE_REG_SYNC(pstcLin->unLAMSRC.u8Register, unLAMSRC.u8Register);

        /* Set flag */
        unIntTrigger.stcBits.CompleteChecksum = 1;
    }
    /* Check if a error has occurred */
    if ((unLAMSR.stcField.u1LER != 0) ||
        (unLAMSR.stcField.u1SER != 0))
    {
        /* Read error flags from HW register */
        unLAMESR.u8Register = pstcLin->unLAMESR.u8Register;
        unSSR.u8Register    = pstcLin->unSSR.u8Register;

        /* Copy error flags */
        unDetectedError.stcBits.BusError      = unLAMESR.stcField.u1LBSER;
        unDetectedError.stcBits.ChecksumError = unLAMESR.stcField.u1LCSER;
        unDetectedError.stcBits.IdParityError = unLAMESR.stcField.u1LPTER;
        unDetectedError.stcBits.OverunError   = unSSR.stcField.u1ORE;
        unDetectedError.stcBits.FramingError  = unSSR.stcField.u1FRE;

        /* Clear possible errors */
        /* LIN assist mode error status */
        unLAMESRC.stcField.u1LBSERC = 1;    /* bus error */
        unLAMESRC.stcField.u1LCSERC = 1;    /* checksum error */
        unLAMESRC.stcField.u1LPTERC = 1;    /* ID parity error */
        PDL_WRITE_REG_SYNC(pstcLin->unLAMESRC.u8Register, unLAMESRC.u8Register);
        unSSRS.stcField.u1RECS = 1;         /* Framing error and overrun error */
        PDL_WRITE_REG_SYNC(pstcLin->unSSRS.u8Register, unSSRS.u8Register);

        /* Set flag */
        unIntTrigger.stcBits.DetectError = 1;
    }

    /* If error has occurred */
    if (unIntTrigger.stcBits.DetectError != 0)
    {
        /* Disable Tx/Rx and clear transmission, reception data */
        (void)Mfs_Lin_DisableTx(pstcLin);
        (void)Mfs_Lin_DisableRx(pstcLin);
    }
    /* If checksum calculation is completed. */
    else if (unIntTrigger.stcBits.CompleteChecksum != 0)
    {
        /* Read data from HW FIFO */
        u8DataCnt = 0;
        while (pstcLin->unFBYTE.stcField.u8FBYTE2 > 0)
        {
            u8Data = pstcLin->unRDR.u8Register;
            /* Check if buffer overrun (Normaly does not occur) */
            if (u8DataCnt < LIN_DATA_LENGTH_MAX)
            {
                pstcLinInternData->au8ReceptionData[u8DataCnt] = u8Data;
            }
            u8DataCnt++;
        }
        /* Check if buffer overrun (Normaly does not occur) */
        if (u8DataCnt <= LIN_DATA_LENGTH_MAX)
        {
            pstcLinInternData->u8ReceivedDataLength = u8DataCnt;
        }
        else
        {
            pstcLinInternData->u8ReceivedDataLength = LIN_DATA_LENGTH_MAX;
        }
    }
    else
    {
        /* do nothing */
    }

    /* Check if possible interrupt trigger has consisted */
    if (unIntTrigger.u8Byte != 0)
    {
        /* Call callback function */
        if (pstcLinInternData->pfnStatusCbFunction != NULL)
        {
            pstcLinInternData->pfnStatusCbFunction(unIntTrigger, unDetectedError);
        }
    }
}


/**
 ******************************************************************************
 ** \brief LIN Initialize FIFO to use
 **
 ** \param [in] pstcUart             Pointer to LIN instance register area
 ******************************************************************************/
static void MfsLinFifoUseInit(pstc_lin_type_t pstcLin)
{
    un_cpg_mfsn_uart_fcr0_t unFCR0   = { 0 };
    un_cpg_mfsn_uart_fcr1_t unFCR1   = { 0 };
    un_cpg_mfsn_uart_fbyte_t unFBYTE = { 0 };

    /*
     *  Local FIFO Control Register 0 variable
     */
    unFCR0.stcField.u1FLD  = 0;    /* No reload (for transmission)   */
    unFCR0.stcField.u1FSET = 0;    /* FIFO2 read pointer not saved    */
    unFCR0.stcField.u1FCL2 = 1;    /* Reset FIFO2 */
    unFCR0.stcField.u1FCL1 = 1;    /* Reset FIFO1 */
    unFCR0.stcField.u1FE2  = 0;    /* Disable FIFO2 */
    unFCR0.stcField.u1FE1  = 0;    /* Disable FIFO1 */

    /*
     *  Local FIFO Control Register 1 variable
     */
    unFCR1.stcField.u1FLSTE = 0;   /* disable data lost detection */
    unFCR1.stcField.u1FRIIE = 0;   /* disable FIFO idle detection */
    unFCR1.stcField.u1FDRQ  = 0;   /* no request for transmission FIFO data */
    unFCR1.stcField.u1FTIE  = 0;   /* disable transmission FIFO interrupt */
    unFCR1.stcField.u1FSEL  = 0;   /* FIFO1: transmission FIFO, FIFO2: reception FIFO */

    /* Setup hardware */
    pstcLin->unFCR0.u8Register  = unFCR0.u8Register;
    pstcLin->unFCR1.u8Register  = unFCR1.u8Register;

    /* Set reception FIFO size to maximum reception data length. */
    unFBYTE.stcField.u8FBYTE2 = LIN_DATA_LENGTH_MAX;
    pstcLin->unFBYTE.u16Register = unFBYTE.u16Register;
}


/**
 *****************************************************************************
 ** \brief Initialisation of a LIN module.
 ** This Function initialises the LIN according the Options setup in the
 ** passed Config Struct. Several Checkings are done before that and an error
 ** is returned if invalid Modes are requested.
 **
 ** The required timing settings for data rate are calculated automatically
 ** with respect to the current peripheral clock.
 **
 ** \pre The Application must configure corresponding LIN pins (SIN, SOT)
 **      according to requirements and settings of LIN instance.
 **
 ** \param [in]  pstcUart       Pointer to LIN instance register area
 ** \param [in]  pstcConfig     LIN module configuration. See #stc_lin_config_t.
 **
 ** \retval Ok                    Initializiation of LIN module successfully done.
 ** \retval ErrorInvalidParameter If one of following conditions are met:
 **         - pstcUart == NULL
 **         - pstcConfig == NULL
 **         - pstcConfig->pfnStatusCb == NULL
 **         - pstcUartInternData == NULL (pstcUart is an invalid address)
 **            i.e. invalid or disabled UART unit (PDL_PERIPHERAL_ENABLE_UART)
 **         - In slave mode, pstcConfig->u16BgrLowerBound or pstcConfig->u16BgrUpperBound
 **           is invalid. Valid range is between LIN_BGR_MIN..LIN_BGR_MAX.
 **           And pstcConfig->u16BgrUpperBound must be greater than or equal to
 **           pstcConfig->u16BgrLowerBound.
 **         - Baud rate calculation error.
 **             pstcConfig->DataRate = 0
 **             Calculated reload value is out of valid range.
 **             (Valid range is between LIN_BGR_MIN to LIN_BGR_MAX)
 *****************************************************************************/
en_result_t Mfs_Lin_Init(pstc_lin_type_t pstcLin,
                     const stc_lin_config_t *pstcConfig)
{
    stc_lin_intern_data_t       *pstcLinInternData;
    uint32_t                    u32ReloadValue;
    en_result_t                 enResult;
    uint8_t                     u8Cnt;

    /* Preset local register variable to zero */
    un_cpg_mfsn_lin_scr_t       unSCR    = { 0 };
    un_cpg_mfsn_lin_scrs_t      unSCRS   = { 0 };
    un_cpg_mfsn_lin_smr_t       unSMR    = { 0 };
    un_cpg_mfsn_lin_escr_t      unESCR   = { 0 };
    un_cpg_mfsn_lin_fcr0c_t     unFCR0C  = { 0 };
    un_cpg_mfsn_lin_sacsr_t     unSACSR  = { 0 };
    un_cpg_mfsn_lin_sacsrc_t    unSACSRC = { 0 };
    un_cpg_mfsn_lin_lamcr_t     unLAMCR  = { 0 };
    un_cpg_mfsn_lin_ecr_t       unECR    = { 0 };
    un_cpg_mfsn_lin_lamier_t    unLAMIER = { 0 };

    pstcLinInternData = MfsLinGetInternDataPtr(pstcLin);
    /* Check if pointers are valid */
    if ((pstcLinInternData == NULL) ||
         (pstcLin == NULL)          ||
         (pstcConfig == NULL)       ||
         (pstcConfig->pfnStatusCb == NULL))
    {
        return ErrorInvalidParameter;
    }

    /* Check only when selected mode is slave mode. */
    if (pstcConfig->bMasterMode == FALSE)
    {
        /* Check if the value is over the maximum. */
        if ((pstcConfig->u16BgrLowerLimit > LIN_BGR_MAX) ||
            (pstcConfig->u16BgrUpperLimit > LIN_BGR_MAX))
        {
            return ErrorInvalidParameter;
        }
        /* Check if the value is under the minimum. */
        if ((pstcConfig->u16BgrLowerLimit < LIN_BGR_MIN) ||
            (pstcConfig->u16BgrUpperLimit < LIN_BGR_MIN))
        {
            return ErrorInvalidParameter;
        }
        if (pstcConfig->u16BgrLowerLimit > pstcConfig->u16BgrUpperLimit)
        {
            return ErrorInvalidParameter;
        }
    }

    /* Calculate baud rate setting */
    enResult = Mfs_Lin_GetReloadValue(pstcConfig->u32DataRate, &u32ReloadValue);
    if (enResult != Ok)
    {
        return enResult;
    }

    /* First of all disable TX and RX for safe operation */
    pstcLin->unSCR.u8Register = 0;

    /* Then disable FIFOs */
    unFCR0C.stcField.u1FE1C = 1;
    unFCR0C.stcField.u1FE2C = 1;
    pstcLin->unFCR0C.u8Register = unFCR0C.u8Register;

    /* Next issue programmable clear */
    unSCRS.stcField.u1UPCLS = 1;
    pstcLin->unSCRS.u8Register = unSCRS.u8Register;


    /* Stop bit length */
    switch(pstcConfig->enStopBit)
    {
        case LinOneStopBit:
            unSMR.stcField.u1SBL = 0;
            unESCR.stcField.u1ESBL = 0;
            break;
        case LinTwoStopBit:
            unSMR.stcField.u1SBL = 1;
            unESCR.stcField.u1ESBL = 0;
            break;
        case LinThreeStopBit:
            unSMR.stcField.u1SBL = 0;
            unESCR.stcField.u1ESBL = 1;
            break;
        case LinFourStopBit:
            unSMR.stcField.u1SBL = 1;
            unESCR.stcField.u1ESBL = 1;
            break;
        default:
            return ErrorInvalidParameter;   /* Should not see daylight */
    }

    /* Select LIN mode */
    unSMR.stcField.u3MD = LIN_MODE_SELECT_LIN;
    /* Enable serial output */
    unSMR.stcField.u1SOE = 1;
    /* now setup hardware */
    pstcLin->unSMR.u8Register = unSMR.u8Register;

    /* Clear AUTE bit */
    unSACSRC.stcField.u1AUTEC = 1;
    /* Clear TMRE bit */
    unSACSRC.stcField.u1TMREC = 1;
    /* now setup register */
    pstcLin->unSACSRC.u16Register = unSACSRC.u16Register;

    if (pstcConfig->bMasterMode != FALSE)
    {
        /* Master mode */
        unSCR.stcField.u1MS = 0;

        /* Baud rate adjustment is disabled. */
        unSACSR.stcField.u1AUTE = 0;

        /* Break field length */
        unESCR.stcField.u2LBL1_0 =
                (pstcConfig->enBreakFieldLength & LIN_ESCR_MASK_LBL1_0);
        unESCR.stcField.u1LBL2 =
                (pstcConfig->enBreakFieldLength >> LIN_ESCR_SHIFT_LBL2);
        /* Break delimiter length */
        unESCR.stcField.u2DEL = pstcConfig->enBreakDelimiterLength;
    }
    else
    {
        /* Slave mode */
        unSCR.stcField.u1MS = 1;

        /* Enable auto baud rate adjustment */
        unSACSR.stcField.u1AUTE = 1;

        /* Sync Field upper/lower limit register */
        pstcLin->unSFUR.u16Register = pstcConfig->u16BgrUpperLimit;
        pstcLin->unSFLR.u16Register = pstcConfig->u16BgrLowerLimit;
    }

    /* now setup HW register(s) */
    pstcLin->unSCR.u8Register = unSCR.u8Register;
    pstcLin->unSACSR.u16Register = unSACSR.u16Register;
    pstcLin->unESCR.u8Register = unESCR.u8Register;

    /* Setup HW FIFO */
    MfsLinFifoUseInit(pstcLin);

    /* Enable assist mode */
    unLAMCR.stcField.u1LAMEN = 1;
    /* Use ID register */
    /* Transfer/reception ID will be stored in LAMTID/LAMRID */
    unLAMCR.stcField.u1LIDEN = 1;
    /* Transmission data clear */
    unLAMCR.stcField.u1LTDRCL = 1;
    /* now setup HW register */
    pstcLin->unLAMCR.u8Register = unLAMCR.u8Register;

    /* Initialize baud rate */
    pstcLin->unBGR.u16Register = (uint16_t)u32ReloadValue;

    /* Issue programmable clear (for LAMCR setting and BGR setting) */
    pstcLin->unSCRS.u8Register = unSCRS.u8Register;

    /* Set serial timer comparison register to maximum value. */
    /* Because, If this value is lower than u16BgrUpperBound, */
    /* auto baud rate adjustment may fail.*/
    pstcLin->unSTMCR.u16Register = 0xFFFF;

    /* Store callback function */
    pstcLinInternData->pfnStatusCbFunction = pstcConfig->pfnStatusCb;
    /* Initialize reception buffer */
    pstcLinInternData->u8ReceivedDataLength = 0;
    for (u8Cnt = 0; u8Cnt < LIN_DATA_LENGTH_MAX; u8Cnt++)
    {
        pstcLinInternData->au8ReceptionData[u8Cnt] = 0;
    }

    /* Interrupt settings */

    /* Output the error interrupt request */
    /* from the error interrupt request pin. */
    unECR.stcField.u1EISEL = 1;
    /* Enable reception error interrupt */
    unECR.stcField.u1REIE = 1;
    /* Disable transmission error interrupt */
    unECR.stcField.u1TEIE = 0;
    /* Other bits are default value */
    /* now setup HW register */
    pstcLin->unECR.u8Register = unECR.u8Register;

    /* Enable auto header completion interrupt */
    unLAMIER.stcField.u1LAHCIE = 1;
    /* Enable checksum calculation completion interrupt */
    unLAMIER.stcField.u1LCSCIE = 1;
    /* Enable LIN bus error detection interrupt */
    unLAMIER.stcField.u1LBSERIE = 1;
    /* note: sync data error does not occur in auto baud rate adjustment mode */
    /* Other bits are default value */
    /* now setup HW register */
    pstcLin->unLAMIER.u8Register = unLAMIER.u8Register;

    /* Enable LIN break detection interrupt */
    pstcLin->unESCR.stcField.u1LBIE = 1;

    /* now TXE and RXE are disabled yet */

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Enable reception.
 **
 ** \note This function enable reception and reception FIFO.
 **
 ** \param  pstcLin        [in]  LIN instance.
 ** \param  u8DataLength   [in]  Data length to be received in bytes.
 ** \param  enChecksumType [in]  Received data Checksum type selection. See #en_lin_checksum_type_t
 **
 ** \retval ErrorInvalidParameter   pstcLin is NULL pointer.
 ** \retval ErrorInvalidParameter   u8DataLength is out of range.
 ** \retval Ok                      Successfully done.
 *****************************************************************************/
en_result_t Mfs_Lin_EnableRx(pstc_lin_type_t pstcLin,
                         uint8_t  u8DataLength,
                         en_lin_checksum_type_t enChecksumType)
{
    un_cpg_mfsn_lin_scrs_t  unSCRS  = { 0 };
    un_cpg_mfsn_lin_fcr0s_t unFCR0S = { 0 };

    /* Check if pointers is valid */
    if (pstcLin == NULL)
    {
        return ErrorInvalidParameter;
    }
    /* Check if data length is valid */
    if (u8DataLength > LIN_DATA_LENGTH_MAX)
    {
        return ErrorInvalidParameter;
    }

    /* Set data length */
    pstcLin->unLAMCR.stcField.u4LDL = u8DataLength;
    /* Set check sum type */
    pstcLin->unLAMCR.stcField.u1LCSTYP = enChecksumType;

    /* Enable reception FIFO */
    unFCR0S.stcField.u1FE2S = 1;
    pstcLin->unFCR0S.u8Register = unFCR0S.u8Register;

    /* Enable reception */
    unSCRS.stcField.u1RXES = 1;
    pstcLin->unSCRS.u8Register = unSCRS.u8Register;

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Disable reception.
 **
 ** \note This function disable reception and clear data waiting to be read.
 **
 ** \param  pstcLin  [in]        LIN instance.
 **
 ** \retval ErrorInvalidParameter   pstcLin is NULL pointer.
 ** \retval Ok                      Successfully done.
 *****************************************************************************/
en_result_t Mfs_Lin_DisableRx(pstc_lin_type_t pstcLin)
{
    volatile uint8_t u8Dummy;
    un_cpg_mfsn_lin_scrc_t  unSCRC  = { 0 };
    un_cpg_mfsn_lin_fcr0c_t unFCR0C = { 0 };
    un_cpg_mfsn_lin_fcr0s_t unFCR0S = { 0 };

    /* Check if pointers is valid */
    if (pstcLin == NULL)
    {
        return ErrorInvalidParameter;
    }

    /* Disable reception */
    unSCRC.stcField.u1RXEC = 1;
    pstcLin->unSCRC.u8Register = unSCRC.u8Register;

    /* Disable reception FIFO */
    unFCR0C.stcField.u1FE2C = 1;
    pstcLin->unFCR0C.u8Register = unFCR0C.u8Register;

    /* Reset reception FIFO */
    unFCR0S.stcField.u1FCL2S = 1;
    pstcLin->unFCR0S.u8Register = unFCR0S.u8Register;

    /* Dummy read to clear RDR */
    u8Dummy = pstcLin->unRDR.u8Register;

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Enable transmission.
 **
 ** \note This function enable transmission and transmission FIFO.
 **
 ** \param  pstcLin  [in]        LIN instance.
 **
 ** \retval ErrorInvalidParameter   pstcLin is NULL pointer.
 ** \retval Ok                      Successfully done.
 *****************************************************************************/
en_result_t Mfs_Lin_EnableTx(pstc_lin_type_t pstcLin)
{
    un_cpg_mfsn_lin_scrs_t  unSCRS  = { 0 };
    un_cpg_mfsn_lin_fcr0s_t unFCR0S = { 0 };

    /* Check if pointer is valid */
    if (pstcLin == NULL)
    {
        return ErrorInvalidParameter;
    }

    /* Enable transmission FIFO */
    unFCR0S.stcField.u1FE1S = 1;
    pstcLin->unFCR0S.u8Register = unFCR0S.u8Register;

    /* Enable transmission */
    unSCRS.stcField.u1TXES = 1;
    pstcLin->unSCRS.u8Register = unSCRS.u8Register;

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Disable transmission.
 **
 ** \note This function disable transmission and clear data waiting to be transmitted.
 **
 ** \param  pstcLin  [in]        LIN instance.
 **
 ** \retval ErrorInvalidParameter   pstcLin is NULL pointer.
 ** \retval Ok                      Successfully done.
 *****************************************************************************/
en_result_t Mfs_Lin_DisableTx(pstc_lin_type_t pstcLin)
{
    un_cpg_mfsn_lin_scrc_t   unSCRC   = { 0 };
    un_cpg_mfsn_lin_fcr0c_t  unFCR0C  = { 0 };
    un_cpg_mfsn_lin_fcr0s_t  unFCR0S  = { 0 };
    un_cpg_mfsn_lin_lamcrs_t unLAMCRS = { 0 };

    /* Check if pointers is valid */
    if (pstcLin == NULL)
    {
        return ErrorInvalidParameter;
    }

    /* Disable transmission */
    unSCRC.stcField.u1TXEC = 1;
    pstcLin->unSCRC.u8Register = unSCRC.u8Register;

    /* Disable transmission FIFO */
    unFCR0C.stcField.u1FE1C = 1;
    pstcLin->unFCR0C.u8Register = unFCR0C.u8Register;

    /* Reset reception FIFO */
    unFCR0S.stcField.u1FCL1S = 1;
    pstcLin->unFCR0S.u8Register = unFCR0S.u8Register;

    /* Clear TDR */
    unLAMCRS.stcField.u1LTDRCLS = 1;
    pstcLin->unLAMCRS.u8Register = unLAMCRS.u8Register;

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Read response data.
 **
 ** \pre Must be run after auto header reception is completed.
 **
 ** \note This function return last received data set.
 **
 ** \param  pstcLin  [in]        LIN instance.
 ** \param  u8Data   [out]       Received data.
 ** \param  u8Length [out]       Data length.
 **
 ** \retval ErrorInvalidParameter   One of the pointers is invalid.
 *****************************************************************************/
en_result_t Mfs_Lin_ReadData(pstc_lin_type_t pstcLin,
                         uint8_t  *u8Data,
                         uint8_t  *u8Length)
{
    stc_lin_intern_data_t *pstcLinInternData;
    uint8_t u8Cnt;

    pstcLinInternData = MfsLinGetInternDataPtr(pstcLin);
    /* Check if pointers are valid */
    if ((pstcLinInternData == NULL) ||
        (pstcLin == NULL)           ||
        (u8Data == NULL)            ||
        (u8Length == NULL))
    {
        return ErrorInvalidParameter;
    }

    /* To ensure data set, disable IRQ */
    IRQ_DISABLE_LOCAL();
    *u8Length = pstcLinInternData->u8ReceivedDataLength;
    for (u8Cnt = 0; u8Cnt < pstcLinInternData->u8ReceivedDataLength; u8Cnt++)
    {
        u8Data[u8Cnt] = pstcLinInternData->au8ReceptionData[u8Cnt];
    }
    IRQ_RESTORE();

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Write response data.
 **        After this function is called, start transmission of data field
 **        immediately.
 **
 ** \pre Must be run after auto header reception completed.
 **
 ** \param  pstcLin        [in]        LIN instance
 ** \param  au8Data        [in]        Pointer to response data.
 ** \param  u8DataLength   [in]        Data length in bytes.
 ** \param  enChecksumType [in]        Checksum type selection. See #en_lin_checksum_type_t
 **
 ** \retval ErrorInvalidParameter      If one of the following conditions are met:
 **                                     - pstcLin or au8Data is NULL
 **                                     - u8DataLength is out of range.
 ** \retval Ok                         Successfully done.
 **
 ** \note   u8DataLength and enChecksumType are effective only when
 **         corresponding LIN instance has been set to slave mode.
 **         In master mode, data length and checksum type has been
 **         set with Mfs_Lin_SetAutoHeader().
 *****************************************************************************/
en_result_t Mfs_Lin_WriteData(pstc_lin_type_t pstcLin,
                          const uint8_t *au8Data,
                          uint8_t u8DataLength,
                          en_lin_checksum_type_t enChecksumType)
{
    uint8_t u8Cnt;

    /* Check if NULL pointer */
    if ((pstcLin == NULL) ||
        (au8Data == NULL))
    {
        return ErrorInvalidParameter;
    }
    /* Check if data length is valid */
    if (u8DataLength > LIN_DATA_LENGTH_MAX)
    {
        return ErrorInvalidParameter;
    }

    /* Enable TX */
    (void)Mfs_Lin_EnableTx(pstcLin);

    /* Check if master mode? */
    /* In master mode, data length and checksum type */
    /* are already set with Lin_SetAutoHeader() */
    if (pstcLin->unSCR.stcField.u1MS != 0)
    {
        /* Use read modify write access not to change other bits */
        /* Set data length */
        pstcLin->unLAMCR.stcField.u4LDL = u8DataLength;
        /* Set checksum type */
        pstcLin->unLAMCR.stcField.u1LCSTYP = enChecksumType;
    }

    if (u8DataLength == 0)
    {
        /* Dummy write to generate Checksum. */
        pstcLin->unRDR.u8Register = 0;
    }
    else
    {
        /* Write data to HW FIFO */
        for (u8Cnt = 0; u8Cnt < u8DataLength; u8Cnt++)
        {
            pstcLin->unRDR.u8Register = au8Data[u8Cnt];
        }
    }

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Start auto header transmission
 **        After this function is called, start transmission of break field,
 **        sync data and PID automatically.
 **
 ** \param  pstcLin      [in]    LIN instance.
 ** \param  u8Id         [in]    Sent ID.
 **                              Parity bits are calculated by HW automatically.
 ** \param  u8DataLength   [in]  Response field data length.
 ** \param  enChecksumType [in]  Checksum type selection. See #en_lin_checksum_type_t
 **
 ** \retval ErrorInvalidMode        If corresponding LIN instance is Slave mode.
 ** \retval ErrorInvalidParameter   ID or Data length is out of range.
 ** \retval Ok                      Start auto header transmission.
 *****************************************************************************/
en_result_t Mfs_Lin_SetAutoHeader(pstc_lin_type_t pstcLin,
                              uint8_t u8Id,
                              uint8_t u8DataLength,
                              en_lin_checksum_type_t enChecksumType)
{
    en_result_t                 enResult;
    un_cpg_mfsn_lin_scrs_t      unSCRS   = { 0 };
    un_cpg_mfsn_lin_lamrid_t    unLAMRID = { 0 };

    /* Check if master mode? */
    if (pstcLin->unSCR.stcField.u1MS != 0)
    {
        enResult = ErrorInvalidMode;
    }
    /* Check if ID is valid */
    else if (u8Id > LIN_ID_MAX)
    {
        enResult = ErrorInvalidParameter;
    }
    /* Check if data length is valid */
    else if (u8DataLength > LIN_DATA_LENGTH_MAX)
    {
        enResult = ErrorInvalidParameter;
    }
    /* valid setting */
    else
    {
        /* Set ID field (Address of LAMTID is same as LAMRID address) */
        /* note: In LIN assist mode, parity bits are calculated by HW. */
        unLAMRID.stcField.u6LID = u8Id;
        pstcLin->unLAMRID.u8Register = unLAMRID.u8Register;

        /* LAMCR settings (Use read modify write access not to change other bits) */
        /* Set data length */
        pstcLin->unLAMCR.stcField.u4LDL = u8DataLength;
        /* Set check sum type */
        pstcLin->unLAMCR.stcField.u1LCSTYP = enChecksumType;

        /* Finally generate LIN Header */
        unSCRS.stcField.u1LBRS = 1;
        pstcLin->unSCRS.u8Register = unSCRS.u8Register;

        enResult = Ok;
    }

    return enResult;
}


/**
 *****************************************************************************
 ** \brief Get received ID and parity
 **
 ** \pre Must be run after auto header reception is completed.
 **
 ** \param  pstcLin  [in]        LIN instance.
 ** \param  u8Id     [out]       Received ID
 ** \param  u8Parity [out]       Calculated parity from received ID
 *****************************************************************************/
en_result_t Mfs_Lin_GetReceivedId(pstc_lin_type_t pstcLin,
                              uint8_t *u8Id,
                              uint8_t *u8Parity)
{
    en_result_t              enResult;
    un_cpg_mfsn_lin_lamrid_t unLAMRID;

    /* Check NULL pointer */
    if ((pstcLin == NULL) ||
        (u8Id == NULL)    ||
        (u8Parity == NULL))
    {
        enResult = ErrorInvalidParameter;
    }
    else
    {
        /* Store received ID and parity bits */
        unLAMRID.u8Register = pstcLin->unLAMRID.u8Register;
        *u8Id               = unLAMRID.stcField.u6LID;
        *u8Parity           = unLAMRID.stcField.u2P;

        enResult = Ok;
    }

    return enResult;
}


/**
 ******************************************************************************
 ** \brief Get reload value
 **        This function return the reload value calculated by
 **        corresponding bus frequency and data rate.
 **
 ** \param [in]  u32DataRate        Baudrate to setup
 ** \param [out] u32ReloadValue     Calculated reload value
 **                                 (Cut off after the decimal point)
 **
 ** \retval Ok                      Calculation is successfully done.
 ** \retval ErrorInvalidParameter   If one of the following conditions are met:
 **                                     - DataRate = 0
 **                                     - u32ReloadValue is NULL pointer
 **                                     - Calculated reload value is out of valid range.
 **                                       (Valid range is between LIN_BGR_MIN to LIN_BGR_MAX)
 ******************************************************************************/
en_result_t Mfs_Lin_GetReloadValue(uint32_t u32DataRate,
                               uint32_t *u32ReloadValue)
{
    uint32_t u32ReloadValueTemp;

    /* Initialize */
    *u32ReloadValue = 0;

    /* Check if data rate is 0 (div by zero not wanted) */
    if (u32DataRate == 0)
    {
        return ErrorInvalidParameter;
    }
    /* Check if data address is valid */
    if (u32ReloadValue == NULL)
    {
        return ErrorInvalidParameter;
    }

    /* Get corresponding bus frequency */
    u32ReloadValueTemp = SysCtrl_GetDistributedClockFrequencyHz(SysCtrlDistributedClockLCP0A);
    /* Calc reload value based on data rate set */
    u32ReloadValueTemp /= u32DataRate;

    /* Check if reload value is 0 (to avoid underflow) */
    if (u32ReloadValueTemp == 0)
    {
        return ErrorInvalidParameter;
    }
    u32ReloadValueTemp -= 1;

    /* Minimum Reload value */
    if (u32ReloadValueTemp < LIN_BGR_MIN)
    {
        return ErrorInvalidParameter;
    }
    /* Maximum Reload value */
    else if (u32ReloadValueTemp > LIN_BGR_MAX)
    {
        return ErrorInvalidParameter;
    }
    else
    {
        /* valid */
        *u32ReloadValue = u32ReloadValueTemp;
    }

    return Ok;
}


/**
 *****************************************************************************
 ** \brief Get the flag which indicates auto baud rate adjustment has been
 **        performed. This function can be run only when corresponding LIN
 **        instance is set to slave mode.
 **        And  bFlag is effective only after the Sync Field is detected.
 **
 ** \param  pstcLin  [in]        LIN instance.
 ** \param  bFlag    [out]       If TRUE, auto baud rate adjustment has been
 **                              performed.
 *****************************************************************************/
en_result_t Mfs_Lin_GetBaudrateAdjustmentFlag(pstc_lin_type_t pstcLin,
                                          boolean_t *bFlag)
{
    en_result_t              enResult;

    /* Check NULL pointer */
    if ((pstcLin == NULL) ||
        (bFlag == NULL))
    {
        enResult = ErrorInvalidParameter;
    }
    else
    {
        /* Check BST bit */
        *bFlag = (pstcLin->unSACSR.stcField.u1BST != 0) ? TRUE : FALSE;
        enResult = Ok;
    }

    return enResult;
}


/** @} */
#endif /* (PDL_PERIPHERAL_LIN_ACTIVE) */

/*****************************************************************************/
/* EOF (not truncated)                                                       */
/*****************************************************************************/


