//###########################################################################
//
// FILE:   sci.c
//
// TITLE:  C28x SCI driver.
//
//###########################################################################
// 
// C2000Ware v5.04.00.00
//
// Copyright (C) 2024 Texas Instruments Incorporated - http://www.ti.com
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   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.
// 
//   Neither the name of Texas Instruments Incorporated 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 
// OWNER 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 "sci.h"

//*****************************************************************************
//
// SCI_setConfig
//
//*****************************************************************************
void
SCI_setConfig(uint32_t base, uint32_t lspclkHz, uint32_t baud, uint32_t config)
{
    uint32_t divider;

    //
    // Check the arguments.
    // Is the required baud rate greater than the maximum rate supported?
    //
    ASSERT(SCI_isBaseValid(base));
    ASSERT(baud != 0U);
    ASSERT((baud * 16U) <= lspclkHz);

    //
    // Stop the SCI.
    //
    SCI_disableModule(base);

    //
    // Compute the baud rate divider.
    //
    divider = ((lspclkHz  / (baud * 8U)) - 1U);

    //
    // Set the baud rate.
    //
    HWREGH(base + SCI_O_HBAUD) = (divider & 0xFF00U) >> 8U;
    HWREGH(base + SCI_O_LBAUD) = divider & 0x00FFU;

    //
    // Set parity, data length, and number of stop bits.
    //
    HWREGH(base + SCI_O_CCR) = ((HWREGH(base + SCI_O_CCR) &
                                 ~(SCI_CONFIG_PAR_MASK |
                                   SCI_CONFIG_STOP_MASK |
                                   SCI_CONFIG_WLEN_MASK)) | config);

    //
    // Start the SCI.
    //
    SCI_enableModule(base);
}

//*****************************************************************************
//
// SCI_writeCharArray
//
//*****************************************************************************
void
SCI_writeCharArray(uint32_t base, const uint16_t * const array,
                   uint16_t length)
{
    //
    // Check the arguments.
    //
    ASSERT(SCI_isBaseValid(base));

    uint16_t i;
    //
    // Check if FIFO enhancement is enabled.
    //
    if(SCI_isFIFOEnabled(base))
    {
        //
        // FIFO is enabled.
        // For loop to write (Blocking) 'length' number of characters
        //
        for(i = 0U; i < length; i++)
        {
            //
            // Wait until space is available in the transmit FIFO.
            //
            while(SCI_getTxFIFOStatus(base) == SCI_FIFO_TX16)
            {
            }

            //
            // Send a char.
            //
            HWREGH(base + SCI_O_TXBUF) = array[i];
        }
    }
    else
    {
        //
        // FIFO is not enabled.
        // For loop to write (Blocking) 'length' number of characters
        //
        for(i = 0U; i < length; i++)
        {
            //
            // Wait until space is available in the transmit buffer.
            //
            while(!SCI_isSpaceAvailableNonFIFO(base))
            {
            }

            //
            // Send a char.
            //
            HWREGH(base + SCI_O_TXBUF) = array[i];
        }
    }
}

//*****************************************************************************
//
// SCI_readCharArray
//
//*****************************************************************************
void
SCI_readCharArray(uint32_t base, uint16_t * const array, uint16_t length)
{
    //
    // Check the arguments.
    //
    ASSERT(SCI_isBaseValid(base));

    uint16_t i;
    //
    // Check if FIFO enhancement is enabled.
    //
    if(SCI_isFIFOEnabled(base))
    {
        //
        // FIFO is enabled.
        // For loop to read (Blocking) 'length' number of characters
        //
        for(i = 0U; i < length; i++)
        {
            //
            // Wait until a character is available in the receive FIFO.
            //
            while(SCI_getRxFIFOStatus(base) == SCI_FIFO_RX0)
            {
            }

            //
            // Return the character from the receive buffer.
            //
            array[i] = (uint16_t)
                       (HWREGH(base + SCI_O_RXBUF) & SCI_RXBUF_SAR_M);
        }
    }
    else
    {
        //
        // FIFO is not enabled.
        // For loop to read (Blocking) 'length' number of characters
        //
        for(i = 0U; i < length; i++)
        {
            //
            // Wait until a character is available in the receive buffer.
            //
            while(!SCI_isDataAvailableNonFIFO(base))
            {
            }

            //
            // Return the character from the receive buffer.
            //
            array[i] = (uint16_t)
                       (HWREGH(base + SCI_O_RXBUF) & SCI_RXBUF_SAR_M);
        }
    }
}

//*****************************************************************************
//
// SCI_enableInterrupt
//
//*****************************************************************************
void
SCI_enableInterrupt(uint32_t base, uint32_t intFlags)
{
    //
    // Check the arguments.
    //
    ASSERT(SCI_isBaseValid(base));

    //
    // Enable the specified interrupts.
    //
    if((intFlags & SCI_INT_RXERR) == SCI_INT_RXERR)
    {
        HWREGH(base + SCI_O_CTL1) |= SCI_CTL1_RXERRINTENA;
    }
    if((intFlags & SCI_INT_RXRDY_BRKDT) == SCI_INT_RXRDY_BRKDT)
    {
        HWREGH(base + SCI_O_CTL2) |= SCI_CTL2_RXBKINTENA;
    }
    if((intFlags & SCI_INT_TXRDY) == SCI_INT_TXRDY)
    {
        HWREGH(base + SCI_O_CTL2) |= SCI_CTL2_TXINTENA;
    }
    if((intFlags & SCI_INT_TXFF) == SCI_INT_TXFF)
    {
        HWREGH(base + SCI_O_FFTX) |= SCI_FFTX_TXFFIENA;
    }
    if((intFlags & SCI_INT_RXFF) == SCI_INT_RXFF)
    {
        HWREGH(base + SCI_O_FFRX) |= SCI_FFRX_RXFFIENA;
    }
}

//*****************************************************************************
//
// SCI_disableInterrupt
//
//*****************************************************************************
void
SCI_disableInterrupt(uint32_t base, uint32_t intFlags)
{
    //
    // Check the arguments.
    //
    ASSERT(SCI_isBaseValid(base));

    //
    // Disable the specified interrupts.
    //
    if((intFlags & SCI_INT_RXERR) == SCI_INT_RXERR)
    {
        HWREGH(base + SCI_O_CTL1) &= ~SCI_CTL1_RXERRINTENA;
    }
    if((intFlags & SCI_INT_RXRDY_BRKDT) == SCI_INT_RXRDY_BRKDT)
    {
        HWREGH(base + SCI_O_CTL2) &= ~SCI_CTL2_RXBKINTENA;
    }
    if((intFlags & SCI_INT_TXRDY) == SCI_INT_TXRDY)
    {
        HWREGH(base + SCI_O_CTL2) &= ~SCI_CTL2_TXINTENA;
    }
    if((intFlags & SCI_INT_TXFF) == SCI_INT_TXFF)
    {
        HWREGH(base + SCI_O_FFTX) &= ~SCI_FFTX_TXFFIENA;
    }
    if((intFlags & SCI_INT_RXFF) == SCI_INT_RXFF)
    {
        HWREGH(base + SCI_O_FFRX) &= ~SCI_FFRX_RXFFIENA;
    }
}

//*****************************************************************************
//
// SCI_getInterruptStatus
//
//*****************************************************************************
uint32_t
SCI_getInterruptStatus(uint32_t base)
{
    uint32_t interruptStatus = 0;

    //
    // Check the arguments.
    //
    ASSERT(SCI_isBaseValid(base));

    //
    // Return the interrupt status.
    //
    if((HWREGH(base + SCI_O_CTL2) & SCI_CTL2_TXRDY) == SCI_CTL2_TXRDY)
    {
        interruptStatus |= SCI_INT_TXRDY;
    }
    if((HWREGH(base + SCI_O_RXST) & SCI_RXST_RXERROR) == SCI_RXST_RXERROR)
    {
        interruptStatus |= SCI_INT_RXERR;
    }
    if(((HWREGH(base + SCI_O_RXST) & SCI_RXST_RXRDY) == SCI_RXST_RXRDY)  ||
       ((HWREGH(base + SCI_O_RXST) & SCI_RXST_BRKDT) ==  SCI_RXST_BRKDT))
    {
        interruptStatus |= SCI_INT_RXRDY_BRKDT;
    }
    if((HWREGH(base + SCI_O_FFTX) & SCI_FFTX_TXFFINT) == SCI_FFTX_TXFFINT)
    {
        interruptStatus |= SCI_INT_TXFF;
    }
    if((HWREGH(base + SCI_O_FFRX) & SCI_FFRX_RXFFINT) == SCI_FFRX_RXFFINT)
    {
        interruptStatus |= SCI_INT_RXFF;
    }
    if((HWREGH(base + SCI_O_RXST) & SCI_RXST_FE) == SCI_RXST_FE)
    {
        interruptStatus |= SCI_INT_FE;
    }
    if((HWREGH(base + SCI_O_RXST) & SCI_RXST_OE) == SCI_RXST_OE)
    {
        interruptStatus |= SCI_INT_OE;
    }
    if((HWREGH(base + SCI_O_RXST) & SCI_RXST_PE) == SCI_RXST_PE)
    {
        interruptStatus |= SCI_INT_PE;
    }

    return(interruptStatus);
}

//*****************************************************************************
//
// SCI_clearInterruptStatus
//
//*****************************************************************************
void
SCI_clearInterruptStatus(uint32_t base, uint32_t intFlags)
{
    //
    // Check the arguments.
    //
    ASSERT(SCI_isBaseValid(base));

    //
    // Clear the requested interrupt sources.
    //
    if(((intFlags & SCI_INT_RXERR) == SCI_INT_RXERR) ||
       ((intFlags & SCI_INT_RXRDY_BRKDT) == SCI_INT_RXRDY_BRKDT) ||
       ((intFlags & SCI_INT_FE) == SCI_INT_FE) ||
       ((intFlags & SCI_INT_OE) == SCI_INT_OE) ||
       ((intFlags & SCI_INT_PE) == SCI_INT_PE))
    {
        SCI_performSoftwareReset(base);
    }
    if((intFlags & SCI_INT_TXFF) == SCI_INT_TXFF)
    {
        HWREGH(base + SCI_O_FFTX) |= SCI_FFTX_TXFFINTCLR;
    }
    if((intFlags & SCI_INT_RXFF) == SCI_INT_RXFF)
    {
         HWREGH(base + SCI_O_FFRX) |= SCI_FFRX_RXFFINTCLR;
    }
}

//*****************************************************************************
//
// SCI_setBaud
//
//*****************************************************************************
void SCI_setBaud(uint32_t base, uint32_t lspclkHz, uint32_t baud)
{
    uint32_t divider;

    //
    // Compute the baud rate divider {ROUND TO NEAREST INTEGER}
    //
    divider = ((float)((float)lspclkHz / ((float)baud * 8.0F)) - 1.0F) + 0.5F;

    //
    // Set the baud rate.
    //
    HWREGH(base + SCI_O_HBAUD) = (divider & 0xFF00U) >> 8U;
    HWREGH(base + SCI_O_LBAUD) = divider & 0x00FFU;
}

//*****************************************************************************
//
// SCI_setWakeFlag
//
//*****************************************************************************
void SCI_setWakeFlag(uint32_t base)
{
    //
    // Check the arguments.
    //
    ASSERT(SCI_isBaseValid(base));

    //
    // Set the TX wake flag bit to indicate
    // that the next frame is an address frame.
    //
    HWREGH(base + SCI_O_CTL1) |= SCI_CTL1_TXWAKE;
}
