/*!
 * @brief Finish up a transfer.
 * Cleans up after a transfer is complete. Interrupts are disabled, and the SPI module
 * is disabled. This is not a public API as it is called from other driver functions.
 */
static void SPI_DRV_MasterCompleteTransfer(uint32_t instance)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* The transfer is complete.*/
    spiState->isTransferInProgress = false;

    /* Disable interrupts */
    SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, false);
    SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (g_spiFifoSize[instance] != 0)
    {
        /* Now disable the SPI FIFO interrupts */
        SPI_HAL_SetFifoIntCmd(base, kSpiTxFifoNearEmptyInt, false);
        SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
    }
#endif

    if (spiState->isTransferBlocking)
    {
        /* Signal the synchronous completion object */
        OSA_SemaPost(&spiState->irqSync);
    }
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_MasterTransferBlocking
 * Description   : Performs a blocking SPI master mode transfer.
 * This function simultaneously sends and receives data on the SPI bus, as SPI is naturally
 * a full-duplex bus. The function will not return until the transfer is complete.
 *
 *END**************************************************************************/
spi_status_t SPI_DRV_MasterTransferBlocking(uint32_t instance,
                                            const spi_master_user_config_t * device,
                                            const uint8_t * sendBuffer,
                                            uint8_t * receiveBuffer,
                                            size_t transferByteCount,
                                            uint32_t timeout)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];
    spi_status_t errorStatus = kStatus_SPI_Success;
    SPI_Type *base = g_spiBase[instance];

    /* fill in members of the run-time state struct*/
    spiState->isTransferBlocking = true; /* Indicates this is a blocking transfer */
    spiState->sendBuffer = (const uint8_t *)sendBuffer;
    spiState->receiveBuffer = (uint8_t *)receiveBuffer;
    spiState->remainingSendByteCount = transferByteCount;
    spiState->remainingReceiveByteCount = transferByteCount;

    /* start the transfer process*/
    errorStatus = SPI_DRV_MasterStartTransfer(instance, device);
    if (errorStatus != kStatus_SPI_Success)
    {
        return errorStatus;
    }

    /* As this is a synchronous transfer, wait until the transfer is complete.*/
    osa_status_t syncStatus;

    do
    {
        syncStatus = OSA_SemaWait(&spiState->irqSync, timeout);
    }while(syncStatus == kStatus_OSA_Idle);

    /* If a timeout occurs, stop the transfer by setting the isTransferInProgress to false and
     * disabling interrupts, then return the timeout error status.
     */
    if (syncStatus != kStatus_OSA_Success)
    {
        /* The transfer is complete.*/
        spiState->isTransferInProgress = false;

        /* Disable interrupts */
        SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, false);
        SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
        if (g_spiFifoSize[instance] != 0)
        {
            /* Now disable the SPI FIFO interrupts */
            SPI_HAL_SetFifoIntCmd(base, kSpiTxFifoNearEmptyInt, false);
            SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
        }
#endif
        errorStatus = kStatus_SPI_Timeout;
    }

    return errorStatus;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveCompleteTransfer
 * Description   : Finish up a transfer.
 * Cleans up after a transfer is complete. Interrupts are disabled, and the SPI module
 * is disabled. This is not a public API as it is called from other driver functions.
 *
 *END**************************************************************************/
static void SPI_DRV_DmaSlaveCompleteTransfer(uint32_t instance)
{
    spi_dma_slave_state_t * spiState = (spi_dma_slave_state_t *)g_spiStatePtr[instance];

    SPI_Type *base = g_spiBase[instance];

    /* Disable DMA requests and interrupts. */
    SPI_HAL_SetRxDmaCmd(base, false);
    SPI_HAL_SetTxDmaCmd(base, false);
    SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);

    /* Stop DMA channels */
    DMA_DRV_StopChannel(&spiState->dmaTransmit);
    DMA_DRV_StopChannel(&spiState->dmaReceive);

    /* Disable interrupts */
    SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, false);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (g_spiFifoSize[instance] != 0)
    {
        /* Now disable the SPI FIFO interrupts */
        SPI_HAL_SetFifoIntCmd(base, kSpiTxFifoNearEmptyInt, false);
        SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
    }

    /* Receive extra byte if remaining receive byte is 0 */
    if ((spiState->hasExtraByte) && (!spiState->remainingReceiveByteCount) &&
        (spiState->receiveBuffer))
    {
        spiState->receiveBuffer[spiState->remainingReceiveByteCount] =
                                                                 SPI_HAL_ReadDataLow(base);
    }
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    if (spiState->isSync)
    {
        /* Signal the synchronous completion object */
        OSA_EventSet(&spiState->event, kSpiDmaTransferDone);
    }

    /* The transfer is complete, update the state structure */
    spiState->isTransferInProgress = false;
    spiState->status = kStatus_SPI_Success;
    spiState->errorCount = 0;
    spiState->sendBuffer = NULL;
    spiState->receiveBuffer = NULL;
    spiState->remainingSendByteCount = 0;
    spiState->remainingReceiveByteCount = 0;
}
/*FUNCTION**********************************************************************
 *
 * Function Name : SPI_DRV_DmaSlaveDeinit
 * Description   : De-initializes the device.
 * Clears the control register and turns off the clock to the module.
 *
 *END**************************************************************************/
spi_status_t SPI_DRV_DmaSlaveDeinit(uint32_t instance)
{
    spi_dma_slave_state_t * spiState = (spi_dma_slave_state_t *)g_spiStatePtr[instance];
    SPI_Type *base = g_spiBase[instance];

    assert(instance < SPI_INSTANCE_COUNT);

    /* Disable SPI interrupt */
    INT_SYS_DisableIRQ(g_spiIrqId[instance]);

    /* Reset the SPI module to its default settings including disabling SPI and its interrupts */
    SPI_HAL_Init(base);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    if (g_spiFifoSize[instance] != 0)
    {
        SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, false);
    }

    /* disable transmit interrupt */
    SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, false);
#endif /* FSL_FEATURE_SPI_16BIT_TRANSFERS */

    /* Free DMA channels */
    DMA_DRV_FreeChannel(&spiState->dmaReceive);
    DMA_DRV_FreeChannel(&spiState->dmaTransmit);

    /* Disable clock for SPI */
    CLOCK_SYS_DisableSpiClock(instance);

    /* Destroy the event */
    OSA_EventDestroy(&spiState->event);

    /* Clear state pointer */
    g_spiStatePtr[instance] = NULL;

    return kStatus_SPI_Success;
}
/*!
 * @brief Initiate (start) a transfer. This is not a public API as it is called from other
 *  driver functions
 */
static spi_status_t SPI_DRV_MasterStartTransfer(uint32_t instance,
                                                const spi_master_user_config_t * device)
{
    /* instantiate local variable of type spi_master_state_t and point to global state */
    spi_master_state_t * spiState = (spi_master_state_t *)g_spiStatePtr[instance];

    uint32_t calculatedBaudRate;
    SPI_Type *base = g_spiBase[instance];

    /* Check that we're not busy.*/
    if (spiState->isTransferInProgress)
    {
        return kStatus_SPI_Busy;
    }

    /* Configure bus for this device. If NULL is passed, we assume the caller has
     * preconfigured the bus using SPI_DRV_MasterConfigureBus().
     * Do nothing for calculatedBaudRate. If the user wants to know the calculatedBaudRate
     * then they can call this function separately.
     */
    if (device)
    {
        SPI_DRV_MasterConfigureBus(instance, device, &calculatedBaudRate);
    }

    /* In order to flush any remaining data in the shift register, disable then enable the SPI */
    SPI_HAL_Disable(base);
    SPI_HAL_Enable(base);

#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    spi_data_bitcount_mode_t bitCount;

    bitCount = SPI_HAL_Get8or16BitMode(base);

    /* Check the transfer byte count. If bits/frame > 8, meaning 2 bytes if bits/frame > 8, and if
     * the transfer byte count is an odd count we'll have to increase the transfer byte count
     * by one and assert a flag to indicate to the receive function that it will need to handle
     * an extra byte so that it does not inadverently over-write an another byte to the receive
     * buffer. For sending the extra byte, we don't care if we read past the send buffer since we
     * are only reading from it and the absolute last byte (that completes the final 16-bit word)
     * is a don't care byte anyway.
     */
    if ((bitCount == kSpi16BitMode) && (spiState->remainingSendByteCount & 1UL))
    {
        spiState->remainingSendByteCount += 1;
        spiState->remainingReceiveByteCount += 1;
        spiState->extraByte = true;
    }
    else
    {
        spiState->extraByte = false;
    }

#endif

    /* If the byte count is zero, then return immediately.*/
    if (spiState->remainingSendByteCount == 0)
    {
        SPI_DRV_MasterCompleteTransfer(instance);
        return kStatus_SPI_Success;
    }

    /* Save information about the transfer for use by the ISR.*/
    spiState->isTransferInProgress = true;
    spiState->transferredByteCount = 0;

    /* Make sure TX data register (or FIFO) is empty. If not, return appropriate
     * error status. This also causes a read of the status
     * register which is required before writing to the data register below.
     */
    if (SPI_HAL_IsTxBuffEmptyPending(base) != 1)
    {
        return kStatus_SPI_TxBufferNotEmpty;
    }


#if FSL_FEATURE_SPI_16BIT_TRANSFERS
    /* If the module/instance contains a FIFO (and if enabled), proceed with FIFO usage for either
     *  8- or 16-bit transfers, else bypass to non-FIFO usage.
     */
    if ((g_spiFifoSize[instance] != 0) && (SPI_HAL_GetFifoCmd(base)))
    {
        /* First fill the FIFO with data */
        SPI_DRV_MasterFillupTxFifo(instance);

        /* If the remaining RX byte count is less than the RX FIFO watermark, enable
         * the TX FIFO empty interrupt. Once the TX FIFO is empty, we are ensured
         * that the transmission is complete and can then drain the RX FIFO.
         * Else, enable the RX FIFO interrupt based on the watermark. In the IRQ
         * handler, another check will be made to see if the remaining RX byte count
         * is less than the RX FIFO watermark.
         */
        if (spiState->remainingReceiveByteCount < g_spiFifoSize[instance])
        {
            SPI_HAL_SetIntMode(base, kSpiTxEmptyInt, true); /* TX FIFO empty interrupt */
        }
        else
        {
            SPI_HAL_SetFifoIntCmd(base, kSpiRxFifoNearFullInt, true);
        }
    }
    /* Modules that support 16-bit transfers but without FIFO support */
    else
    {
        uint8_t byteToSend = 0;

        /* SPI configured for 16-bit transfers, no FIFO */
        if (bitCount == kSpi16BitMode)
        {
            uint8_t byteToSendLow = 0;
            uint8_t byteToSendHigh = 0;

            if (spiState->sendBuffer)
            {
                byteToSendLow = *(spiState->sendBuffer);
                ++spiState->sendBuffer;

                /* If the extraByte flag is set and these are the last 2 bytes, then skip this */
                if (!((spiState->extraByte) && (spiState->remainingSendByteCount == 2)))
                {
                    byteToSendHigh = *(spiState->sendBuffer);
                    ++spiState->sendBuffer;
                }
            }
            SPI_HAL_WriteDataLow(base, byteToSendLow);
            SPI_HAL_WriteDataHigh(base, byteToSendHigh);

            spiState->remainingSendByteCount -= 2;  /* decrement by 2 */
            spiState->transferredByteCount += 2;  /* increment by 2 */
        }
        /* SPI configured for 8-bit transfers, no FIFO */
        else
        {
            if (spiState->sendBuffer)
            {
                byteToSend = *(spiState->sendBuffer);
                ++spiState->sendBuffer;
            }
            SPI_HAL_WriteDataLow(base, byteToSend);

            --spiState->remainingSendByteCount;
            ++spiState->transferredByteCount;
        }

        /* Only enable the receive interrupt.  This should be ok since SPI is a synchronous
         * protocol, so every RX interrupt we get, we should be ready to send. However, since
         * the SPI has a shift register and data buffer (for transmit and receive), things may not
         * be cycle-to-cycle synchronous. To compensate for this, enabling the RX interrupt only
         * ensures that we do indeed receive data before sending out the next data word. In the
         * ISR we make sure to first check for the RX data buffer full before checking the TX
         * data register empty flag.
         */
        SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, true);
    }

#else /* For SPI modules that do not support 16-bit transfers */

    /* Start the transfer by writing the first byte. If a send buffer was provided, the byte
     * comes from there. Otherwise we just send a zero byte. Note that before writing to the
     * data register, the status register must first be read, which was already performed above.
     */
    uint8_t byteToSend = 0;
    if (spiState->sendBuffer)
    {
        byteToSend = *(spiState->sendBuffer);
        ++spiState->sendBuffer;
    }
    SPI_HAL_WriteData(base, byteToSend);

    --spiState->remainingSendByteCount;
    ++spiState->transferredByteCount;

    /* Only enable the receive interrupt.  This should be ok since SPI is a synchronous
     * protocol, so every RX interrupt we get, we should be ready to send. However, since
     * the SPI has a shift register and data buffer (for transmit and receive), things may not
     * be cycle-to-cycle synchronous. To compensate for this, enabling the RX interrupt only
     * ensures that we do indeed receive data before sending out the next data word. In the
     * ISR we make sure to first check for the RX data buffer full before checking the TX
     * data register empty flag.
     */
    SPI_HAL_SetIntMode(base, kSpiRxFullAndModfInt, true);
#endif

    return kStatus_SPI_Success;
}