/**************************************************************************
 * @file     pan_spi.h
 * @version  V1.10
 * $Revision: 2 $
 * $Date:    2023/11/08  $  
 * @brief    PAN series SPI driver header file
 *
 * @note
 * Copyright (C) 2025 Panchip Technology Corp. All rights reserved.
 *****************************************************************************/

#ifndef __PAN_SPI_H__
#define __PAN_SPI_H__

/**
 * @brief Spi Interface
 * @defgroup spi_interface Spi Interface
 * @{
 */

#ifdef __cplusplus
extern "C"
{
#endif

#include "pan271x.h"

/**@defgroup SPI_IRQ_FLAG Spi interrupt event type
 * @brief       Spi interrupt event type constants definitions
 * @{ */ 
typedef enum SPI_Irq
{
    SPI_IRQ_TX_HALF_EMPTY   = 0x08,     //Transmit FIFO Half Empty Interrupt
    SPI_IRQ_RX_HALF_FULL    = 0x04,     //Receive FIFO Half Full Interrupt
    SPI_IRQ_RX_TIMEOUT      = 0x02,     //Receive FIFO Not Empty and No Read prior to Timeout Interrupt
    SPI_IRQ_RX_OVERRUN      = 0x01,     //Receive FIFO Overflow Interrupt
    SPI_IRQ_ALL             = 0x0f      //All interrupts
} SPI_IrqDef;
/**@} */

/**@defgroup SPI_WORK_MODE_FLAG Spi operating mode
 * @brief       Spi operating mode constants definitions
 * @{ */ 
typedef enum SPI_Role
{
    SPI_RoleMaster  = 0x0,
    SPI_RoleSlave   = 0x1
} SPI_RoleDef;  
/**@} */

#if 0
/*Transfer Mode. Selects the mode of transfer for serial communication. This field
does not affect the transfer duplicity. Only indicates whether the receive or transmit
data are valid.*/
typedef enum SPI_TransferMode
{
    Spi_dir_full_duplex = 0x0,
    Spi_dir_tx_only     = 0x1,
    Spi_dir_rx_only     = 0x2,
    Spi_dir_read_eeprom = 0x3
} SPI_TransferModeDef;
#endif

/**@defgroup SPI_DATA_SIZE_FLAG Spi data frame size
 * @brief       Spi data frame size constants definitions
 * @{ */ 
typedef enum SPI_DataFrameSize
{
    SPI_DataFrame_4b    = 0x03,
    SPI_DataFrame_5b    = 0x04,
    SPI_DataFrame_6b    = 0x05,
    SPI_DataFrame_7b    = 0x06,
    SPI_DataFrame_8b    = 0x07,
    SPI_DataFrame_9b    = 0x08,
    SPI_DataFrame_10b   = 0x09,
    SPI_DataFrame_11b   = 0x0a,
    SPI_DataFrame_12b   = 0x0b,
    SPI_DataFrame_13b   = 0x0c,
    SPI_DataFrame_14b   = 0x0d,
    SPI_DataFrame_15b   = 0x0e,
    SPI_DataFrame_16b   = 0x0f
} SPI_DataFrameSizeDef;
/**@} */

/**@defgroup SPI_BAUD_DIV_FLAG Spi baudrate divider
 * @brief       Spi baudrate divider constants definitions
 * @{ */
typedef enum SPI_BaudRateDiv
{
    SPI_BaudRateDiv_2   = 0x02,
    SPI_BaudRateDiv_4   = 0x04,
    SPI_BaudRateDiv_6   = 0x06,
    SPI_BaudRateDiv_8   = 0x08,
    SPI_BaudRateDiv_16  = 0x10,
    SPI_BaudRateDiv_32  = 0x20,
    SPI_BaudRateDiv_48  = 0x30,
    SPI_BaudRateDiv_64  = 0x40,
    SPI_BaudRateDiv_96  = 0x60,
    SPI_BaudRateDiv_128 = 0x80,
    SPI_BaudRateDiv_160 = 0xA0,
    SPI_BaudRateDiv_192 = 0xC0,
    SPI_BaudRateDiv_224 = 0xE0,
    SPI_BaudRateDiv_240 = 0xF0,
    SPI_BaudRateDiv_250 = 0xFA,
} SPI_BaudRateDivDef;
/**@} */

#define SPI_BAUD_RATE_DIV(x)	((x)?(x):(256))

/**@defgroup SPI_POLARITY_FLAG Spi clock polarity
 * @brief       Spi clock polarity constants definitions,Serial Clock Polarity, Used to 
 *							select the polarity of the inactive serial clock, which is held inactive when
 *							the DW_apb_ssi master is not actively transferring data on the serial bus
 * @{ */
typedef enum SPI_ClockPol
{
    SPI_ClockPolarityLow    = 0x0,
    SPI_ClockPolarityHigh   = 0x1
} SPI_ClockPolDef;
/**@} */

/**@defgroup SPI_PHASE_FLAG Spi clock phase
 * @brief       Spi clock phase constants definitions
 * @{ */
typedef enum SPI_ClockPhase
{
    SPI_ClockPhaseFirstEdge     = 0x0,
    SPI_ClockPhaseSecondEdge    = 0x1
} SPI_ClockPhaseDef;
/**@} */

/**@defgroup SPI_FORMAT_FLAG Spi frame format
 * @brief       Spi frame format constants definitions
 * @{ */
typedef enum SPI_FrameFormat
{
    SPI_FormatMotorola     = 0x00,
    SPI_FormatTi           = 0x01,
    SPI_FormatMicrowire    = 0x02,
} SPI_FrameFormatDef;
/**@} */

/**
 * @brief Enumeration of SPI Wire Number.
 */
typedef enum SPI_WireNum
{
    SPI_WireNumThree  = 0x01, /*!< 3-wire SPI communication */
    SPI_WireNumFour   = 0x00  /*!< 4-wire SPI communication */
} SPI_WireNumDef;

/**
 * @brief Enumeration of SPI Command/Data Selection.
 */
typedef enum SPI_CmdDat
{
    SPI_CommandSelect  = 0x00, /*!< SPI command selection */
    SPI_DataSelect     = 0x01  /*!< SPI data selection */
} SPI_CmdDatDef;


/**
 * @brief Enumeration of SPI Write/Read Selection.
 */
typedef enum SPI_WriteReadSel
{
    SPI_3WireRead    = 0x00, /*!< 3-wire SPI read selection */
    SPI_3WireWrite   = 0x01  /*!< 3-wire SPI write selection */
} SPI_WriteReadSelDef;

/** 
 * @brief Structure with spi init feature.
 *
 * @param SPI_role 						Specifies the SPI operating mode.\ref SPI_WORK_MODE_FLAG
 * @param SPI_dataFrameSize		Specifies the SPI data size	\ref SPI_DATA_SIZE_FLAG
 * @param SPI_CPOL						Specifies the SPI data size \ref SPI_POLARITY_FLAG
 * @param SPI_CPHA						Specifies the clock active edge for the bit capture \ref SPI_PHASE_FLAG
 * @param SPI_baudRateDiv			Specifies the Baud Rate Divisor used to configure the transmit and receive
 *																 SCK clock \ref SPI_BAUD_DIV_FLAG
 * @param SPI_format					Specifies the SPI format \ref SPI_FORMAT_FLAG
 */
typedef struct
{
    SPI_RoleDef SPI_role;                       /*!< Specifies the SPI operating mode */
    SPI_DataFrameSizeDef SPI_dataFrameSize;     /*!< Specifies the SPI data size */
    SPI_ClockPolDef SPI_CPOL;                   /*!< Specifies the serial clock steady state */
    SPI_ClockPhaseDef SPI_CPHA;                 /*!< Specifies the clock active edge for the bit capture */
    uint16_t SPI_baudRateDiv;         /*!< Specifies the Baud Rate Divisor used to configure the transmit and receive SCK clock. */
} SPI_InitTypeDef;


/**
  * @brief  Synchronous serial port enable
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval None
  */
__STATIC_INLINE void SPI_EnableSpi(SPI_T* SPIx)
{
    uint32_t tmpreg = 0;

    tmpreg = SPIx->SSPCR1;
    tmpreg |= SSPCR1_SSE_Msk;
    SPIx->SSPCR1 = tmpreg;
}

/**
  * @brief  Synchronous serial port disable
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval None
  */
__STATIC_INLINE void SPI_DisableSpi(SPI_T* SPIx)
{
    uint32_t tmpreg = 0;

    tmpreg = SPIx->SSPCR1;
    tmpreg &= ~SSPCR1_SSE_Msk;
    SPIx->SSPCR1 = tmpreg;
}

/**
  * @brief  get synchronous serial port status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval true/false
  */
__STATIC_INLINE bool SPI_IsSpiEnabled(SPI_T* SPIx)
{
    uint32_t tmpreg = 0;

    tmpreg = SPIx->SSPCR1;
    if(tmpreg & SSPCR1_SSE_Msk)
        return true;
    else
        return false;
}


/**
  * @brief  Send data,SSPDR is the data register and is 16-bits wide
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval None
  */
__STATIC_INLINE void SPI_SendData(SPI_T* SPIx, uint32_t Data)
{
    SPIx->SSPDR = Data;
}

/**
  * @brief  Receive data,SSPDR is the data register and is 16-bits wide
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval None
  */
__STATIC_INLINE uint32_t SPI_ReceiveData(SPI_T* SPIx)
{
    return SPIx->SSPDR;
}

/**
  * @brief  Interruput mask Enable
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @param  irq: where x can be 
  *          SPI_IRQ_TX_HALF_EMPTY 
  *          SPI_IRQ_RX_HALF_FULL 
  *          SPI_IRQ_RX_TIMEOUT
  *          SPI_IRQ_RX_OVERRUN
  *          SPI_IRQ_ALL 
  * @retval None
  */
__STATIC_INLINE void SPI_EnableIrq(SPI_T* SPIx, SPI_IrqDef irq)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPIMSC;
    tmpreg |= irq;
    SPIx->SSPIMSC = tmpreg;
}

/**
  * @brief  Interruput mask Disable
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @param  irq: where x can be 
  *          \ref SPI_IRQ_TX_HALF_EMPTY 
  *          \ref SPI_IRQ_RX_HALF_FULL 
  *          \ref SPI_IRQ_RX_TIMEOUT
  *          \ref SPI_IRQ_RX_OVERRUN
  *          \ref SPI_IRQ_ALL 
  * @return None
  */
__STATIC_INLINE void SPI_DisableIrq(SPI_T* SPIx, SPI_IrqDef irq)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPIMSC;
    tmpreg &= ~irq;
    SPIx->SSPIMSC = tmpreg;
}

/**
  * @brief  Get Interruput mask status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @param  irq: where x can be 
  *          \ref SPI_IRQ_TX_HALF_EMPTY 
  *          \ref SPI_IRQ_RX_HALF_FULL 
  *          \ref SPI_IRQ_RX_TIMEOUT
  *          \ref SPI_IRQ_RX_OVERRUN
  *          \ref SPI_IRQ_ALL 
  * @return None
  */
__STATIC_INLINE bool SPI_IsIrqEnabled(SPI_T* SPIx, SPI_IrqDef irq)
{
    uint32_t tmpreg;

    tmpreg = SPIx->SSPIMSC;
    if(tmpreg & irq )
        return true;
    else
        return false;
}

/**
  * @brief  Get Masked Interruput status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral. 
  * @retval SPI_IrqDef: where return value can be 
  *          SPI_IRQ_TX_HALF_EMPTY 
  *          SPI_IRQ_RX_HALF_FULL 
  *          SPI_IRQ_RX_TIMEOUT
  *          SPI_IRQ_RX_OVERRUN
  *          SPI_IRQ_ALL 
  */
__STATIC_INLINE SPI_IrqDef SPI_GetMskedActiveIrq(SPI_T* SPIx)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPMIS;
    tmpreg &= SPI_IRQ_ALL;

    return (SPI_IrqDef)tmpreg;
}

/**
  * @brief  Get Masked Interruput status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @param  irq: where x can be 
  *          \ref SPI_IRQ_TX_HALF_EMPTY 
  *          \ref SPI_IRQ_RX_HALF_FULL 
  *          \ref SPI_IRQ_RX_TIMEOUT
  *          \ref SPI_IRQ_RX_OVERRUN
  *          \ref SPI_IRQ_ALL 
  * @return true/false
  */
__STATIC_INLINE bool SPI_IsIrqActive(SPI_T * SPIx, SPI_IrqDef irq)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPMIS;
    if(tmpreg & irq )
        return true;
    else
        return false;
}

/**
  * @brief  Get raw Interruput status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @param  irq: where x can be 
  *          \ref SPI_IRQ_TX_HALF_EMPTY 
  *          \ref SPI_IRQ_RX_HALF_FULL 
  *          \ref SPI_IRQ_RX_TIMEOUT
  *          \ref SPI_IRQ_RX_OVERRUN
  *          \ref SPI_IRQ_ALL 
  * @return true/false
  */
__STATIC_INLINE bool SPI_IsRawIrqActive(SPI_T * SPIx, SPI_IrqDef irq)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPRIS;
    if(tmpreg & irq )
        return true;
    else
        return false;
}

/**
  * @brief  clear Interruput status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @param  irq: can be 
  *          \ref SPI_IRQ_RX_TIMEOUT
  *          \ref SPI_IRQ_RX_OVERRUN
  * @return None
  */
__STATIC_INLINE void SPI_ClearIrq(SPI_T * SPIx, SPI_IrqDef irq)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPICR;
    tmpreg |= irq ;
    SPIx->SSPICR = tmpreg;
}
/**
  * @brief  Get Raw Interruput status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral. 
  * @return SPI_IrqDef: where return value can be 
  *          \ref SPI_IRQ_TX_HALF_EMPTY 
  *          \ref SPI_IRQ_RX_HALF_FULL 
  *          \ref SPI_IRQ_RX_TIMEOUT
  *          \ref SPI_IRQ_RX_OVERRUN
  *          \ref SPI_IRQ_ALL 
  */
__STATIC_INLINE SPI_IrqDef SPI_GetRawActiveIrq(SPI_T* SPIx)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPRIS;
    tmpreg &= SPI_IRQ_ALL;

    return (SPI_IrqDef)tmpreg;
}

/**
  * @brief  Get Tx FIFO empty status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval true/false
  */
__STATIC_INLINE bool SPI_IsTxFifoEmpty(SPI_T* SPIx)
{
    uint32_t tmpreg = 0x00;
    tmpreg = SPIx->SSPSR;

    if(tmpreg & SSPSR_TFE_Msk)
        return true;
    else
        return false;
}
/**
  * @brief  Get Tx FIFO full status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval true/false
  */
__STATIC_INLINE bool SPI_IsTxFifoFull(SPI_T* SPIx)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPSR;

    if(tmpreg & SSPSR_TNF_Msk)
        return false;
    else
        return true;
}
/**
  * @brief  Get Tx FIFO empty status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval true/false
  */
__STATIC_INLINE bool SPI_IsRxFifoEmpty(SPI_T* SPIx)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPSR;

    if(tmpreg & SSPSR_RNE_Msk)
        return false;
    else
        return true;

}
/**
  * @brief  Get Tx FIFO full status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval true/false
  */
__STATIC_INLINE bool SPI_IsRxFifoFull(SPI_T* SPIx)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPSR;

    if(tmpreg & SSPSR_RFF_Msk)
        return true;
    else
        return false;

}
/**
  * @brief  Get Synchronous serial port busy status
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @retval true/false
  */
__STATIC_INLINE bool SPI_IsBusy(SPI_T* SPIx)
{
    uint32_t tmpreg = 0x00;

    tmpreg = SPIx->SSPSR;
    if(tmpreg & SSPSR_BSY_Msk)
        return true;
    else
        return false;
}


/**
  * @brief  Initializes the SPIx peripheral according to the specified
  *         parameters in the SPI_InitStruct .
  * @param  SPIx: where x can be 1, 2 to select the
  *         SPI peripheral.
  * @param  SPI_InitStruct: pointer to a SPI_InitTypeDef structure that contains
  *         the configuration information for the specified SPI peripheral.
  * @return Real baudrate
  */
uint32_t SPI_Init(SPI_T* SPIx, SPI_InitTypeDef* SPI_InitStruct);

/**@} */

#ifdef __cplusplus
}
#endif

#endif //__PAN_SPI_H__

/*** (C) COPYRIGHT 2023 Panchip Technology Corp. ***/
