/**
  ******************************************************************************
  * @file    KPM32xx_ddl_spi.h
  * @author  Kiwi Software Team
  * @brief   Header file of SPI DDL module.
  * @note
  *          V1.0.0, 2025/1/7.
  *
  Copyright (c) 2024, Kiwi Instruments Co,. Ltd.
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *
  *   2. 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.
  *
  *   3. Neither the name of the copyright holder 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 HOLDER 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.
  ******************************************************************************
  */



#ifndef __KPM32xx_DDL_SPI_H
#define __KPM32xx_DDL_SPI_H

#ifdef __cplusplus
extern "C" {
#endif

#include "kpm32xx_ddl_def.h"

#define __MAX_CS_CNT__     2

#define __CS_PIN_INDEX0__  0
#define __CS_PIN_INDEX1__  1

#define __SPI_FIFOTX_THRESHOLD__  4
#define __SPI_FIFORX_THRESHOLD__  4

/* CS control config */
typedef struct
{
	uint32_t nssEn;     /* Enable CS pin */
	uint32_t hwCtrl;    /* Hardware control or software control */
} SPI_NssCfg_T;

/* CS timing config */
typedef struct
{
	uint8_t ssDest;    /* SS Delay in SCK unit */
	uint8_t frmDly;    /* Frame Delay in SCK unit */
	uint8_t hldTim;    /* Hold Time in SCK unit */
	uint8_t setTim;    /* Setup Time in SCK unit */
} SPI_SSTimCfg_T;

/* Data frame width. */
typedef enum
{
	SPI_DTWIDTH_08BIT = 7,        /* Data frame width : 8bit */
	SPI_DTWIDTH_16BIT = 15,       /* Data frame width : 16bit */
	SPI_DTWIDTH_32BIT = 31,       /* Data frame width : 32bit */
} DataWidth_T;

typedef struct
{
	uint32_t workMode;                /* Master or Slave mode */
	DataWidth_T frameWidth;           /* 1 ~ 32 bit */
	uint32_t clkPolarity;             /* Clock polarity */
	uint32_t clkPhase;                /* Clock phase */
	SPI_NssCfg_T nss[__MAX_CS_CNT__]; /* CS Pin controlled by software or by module */
	SPI_SSTimCfg_T ssTimCfg;          /* Setup/Hold time configuration */
	uint32_t mlsb;                    /* MSB or LSB first */
	uint32_t transMode;               /* Full Duplex, TX-Only and RX-Only */
	uint32_t baudRate;                /* Baud rate */
} SPI_Init_T;

#define __SPI_08BIT_DUMMY_DATA__   0x5A
#define __SPI_16BIT_DUMMY_DATA__   0x5AA5
#define __SPI_32BIT_DUMMY_DATA__   0x5AA55AA5

/* Work Mode */
#define SPI_MODE_SLAVE                  SPI_CFG_SLAVE
#define SPI_MODE_MASTER                 SPI_CFG_MASTER

/* Polarity and Phase configuration */
#define SPI_CLKPOLARITY_LOW             SPI_CFG_SCKCPOL_LOW
#define SPI_CLKPOLARITY_HIGH            SPI_CFG_SCKCPOL_HIGH
#define SPI_CLKPHASE_LOW                SPI_CFG_SCKCPHA_LOW
#define SPI_CLKPHASE_HIGH               SPI_CFG_SCKCPHA_HIGH

/* CS Pin Controlled either by software or hardware */
#define SPI_NSS_SOFT                    (0x00000001U)
#define SPI_NSS_HARD                    (0x00000002U)

#define SPI_MSB_FIRSTBIT                SPI_CFG_MLSB_MSB
#define SPI_LSB_FIRSTBIT                SPI_CFG_MLSB_LSB

/* Transmission mode: Full Duplex/Single Tx/Single Rx */
#define SPI_FULLDUPLEX_TRANSMODE        SPI_CFG_FULLDUPLEX
#define SPI_SINGLETXONLY_TRANSMODE      SPI_CFG_SINGLETXONLY
#define SPI_SINGLERXONLY_TRANSMODE      SPI_CFG_SINGLERXONLY

/* Get FIFO RX/TX Threshold */
#define __DDL_SPI_GET_FIFOTXLVL(__INSTANCE__)                 ((READ_REG((__INSTANCE__)->FFST) & (SPI_FFST_TXCNT_Msk)) >> SPI_FFST_TXCNT_Pos)
#define __DDL_SPI_GET_FIFORXLVL(__INSTANCE__)                 ((READ_REG((__INSTANCE__)->FFST) & (SPI_FFST_RXCNT_Msk)) >> SPI_FFST_RXCNT_Pos)

#define __DDL_SPI_BAUDRATE_CONFIG(__INSTANCE__, __BAUDRATE__)        WRITE_REG((__INSTANCE__)->BRD, __BAUDRATE__)

/* Enable MultiData Transaction, so that CS can be pulled high when transmission completes */
#define __DDL_SPI_MULTIDATATRANS_CONFIG(__INSTANCE__, __DATACNT__)\
do {\
	if (__DATACNT__ == 0)\
	{\
		WRITE_REG((__INSTANCE__)->FRT,0);\
	}\
	else\
	{\
		MODIFY_REG((__INSTANCE__)->FRT, (SPI_FRT_CNT_Msk | SPI_FRT_MULDT_Msk), ((__DATACNT__ << SPI_FRT_CNT_Pos) | SPI_FRT_MULDT));\
	}\
	WRITE_REG((__INSTANCE__)->INTST,0xFFFFFFFF);\
}\
while (0);

/* Configure Sample Delay Counter */
#define __DDL_SPI_SAMPLEDELAY_CONFIG(__INSTANCE__, __DELAYCNT__)     MODIFY_REG((__INSTANCE__)->DLY, SPI_DLY_CNT_Msk, (__DELAYCNT__ << SPI_DLY_CNT_Pos) )

/* Configure TX And RX FIFO */
#define __DDL_SPI_TXTHRD_CONFIG(__INSTANCE__, __TXTHRD__)  MODIFY_REG((__INSTANCE__)->FFCTL, SPI_FFCTL_TXTH_Msk, (__TXTHRD__ << SPI_FFCTL_TXTH_Pos))
#define __DDL_SPI_RXTHRD_CONFIG(__INSTANCE__, __RXTHRD__)  MODIFY_REG((__INSTANCE__)->FFCTL, SPI_FFCTL_RXTH_Msk, (__RXTHRD__ << SPI_FFCTL_RXTH_Pos))

/* Make TX and RX FiFo empty */
#define __DDL_SPI_CLEAR_TXFIFO(__INSTANCE__)               SET_BIT((__INSTANCE__)->FFCTL, SPI_FFCTL_TXFFCLR)
#define __DDL_SPI_CLEAR_RXFIFO(__INSTANCE__)               SET_BIT((__INSTANCE__)->FFCTL, SPI_FFCTL_RXFFCLR)

/* Master or Slave Work mode */
#define __DDL_SPI_CONFIG_AS_MASTER(__INSTANCE__)           SET_BIT((__INSTANCE__)->CFG, SPI_CFG_WKMOD)
#define __DDL_SPI_CONFIG_AS_SLAVE(__INSTANCE__)            CLEAR_BIT((__INSTANCE__)->CFG, SPI_CFG_WKMOD)

/* Full Duplex & Single-Tx only & Single-Rx only */
#define __DDL_SPI_CONFIG_TRANSMOD(__INSTANCE__, __TRANSMODE__)     MODIFY_REG((__INSTANCE__)->CFG, SPI_CFG_TRMOD_Msk, (__TRANSMODE__))

/* SPI Module Enable */
#define __DDL_SPI_MODULE_ENABLE(__INSTANCE__)              SET_BIT((__INSTANCE__)->CFG, SPI_CFG_EN)
/* SPI Module Disable */
#define __DDL_SPI_MODULE_DISABLE(__INSTANCE__)             CLEAR_BIT((__INSTANCE__)->CFG, SPI_CFG_EN)

/* SPI Global Interrupt Enable */
#define __DDL_SPI_GLOBALIT_ENABLE(__INSTANCE__)            SET_BIT((__INSTANCE__)->INTR, SPI_INTR_EN)
/* SPI Global Interrupt Disable */
#define __DDL_SPI_GLOBALIT_DISABLE(__INSTANCE__)           CLEAR_BIT((__INSTANCE__)->INTR, SPI_INTR_EN)

/* SPI Individual Interrupt Enable */
#define __DDL_SPI_ENABLE_IT(__INSTANCE__, __INTERRUPT__)   SET_BIT((__INSTANCE__)->INTR, (__INTERRUPT__))
/* SPI Individual Interrupt Disable */
#define __DDL_SPI_DISABLE_IT(__INSTANCE__, __INTERRUPT__)  CLEAR_BIT((__INSTANCE__)->INTR, (__INTERRUPT__))

/* Clear Interrupt Flag */
#define __DDL_SPI_CLEAR_INTRFLAG(__INSTANCE__, __FLAG__)   WRITE_REG((__INSTANCE__)->INTST, (__FLAG__))

/* SPI CS Pin Controlled by Software (to High or Low) */
#define __DDL_SPI_PULL_CS_PIN2LOW(__INSTANCE__, __CSINDEX__)   CLEAR_BIT((__INSTANCE__)->CSCTL,  0x1UL << (__CSINDEX__ + SPI_CSCTL_SS0OUT_Pos))
#define __DDL_SPI_PULL_CS_PIN2HIGH(__INSTANCE__, __CSINDEX__)  SET_BIT((__INSTANCE__)->CSCTL,    0x1UL << (__CSINDEX__ + SPI_CSCTL_SS0OUT_Pos))

/*TX and RX DMA Enable*/
#define __DDL_SPI_TXDMA_ENABLE(__INSTANCE__)   SET_BIT((__INSTANCE__)->DMATX, (SPI_DMATX_EN))
#define __DDL_SPI_RXDMA_ENABLE(__INSTANCE__)   SET_BIT((__INSTANCE__)->DMARX, (SPI_DMARX_EN))
#define __DDL_SPI_TXDMA_DISABLE(__INSTANCE__)  CLEAR_BIT((__INSTANCE__)->DMATX, (SPI_DMATX_EN))
#define __DDL_SPI_RXDMA_DISABLE(__INSTANCE__)  CLEAR_BIT((__INSTANCE__)->DMARX, (SPI_DMARX_EN))

/* TX DMA and RX DMA Trigger Level */
#define __DDL_SPI_RXDMALVL_CONFIG(__INSTANCE__, __LVL__)   MODIFY_REG((__INSTANCE__)->DMARX, SPI_DMARX_LVL_Msk, ((__LVL__) << SPI_DMARX_LVL_Pos));
#define __DDL_SPI_TXDMALVL_CONFIG(__INSTANCE__, __LVL__)   MODIFY_REG((__INSTANCE__)->DMATX, SPI_DMATX_LVL_Msk, ((__LVL__) << SPI_DMATX_LVL_Pos));

#define IS_SPI_WORKMODE(__MODE__)      (((__MODE__) == SPI_MODE_SLAVE) || ((__MODE__) == SPI_MODE_MASTER))

#define IS_SPI_TRANSMODE(__MODE__)     (((__MODE__) == SPI_FULLDUPLEX_TRANSMODE)    || \
									    ((__MODE__) == SPI_SINGLETXONLY_TRANSMODE)  || \
									    ((__MODE__) == SPI_SINGLERXONLY_TRANSMODE))

#define IS_SPI_CLKPOLARITY(__CLKPOL__) (((__CLKPOL__) == SPI_CLKPOLARITY_LOW) || ((__CLKPOL__) == SPI_CLKPOLARITY_HIGH))
#define IS_SPI_CLKPHASE(__CLKPHA__)    (((__CLKPHA__) == SPI_CLKPHASE_LOW) || ((__CLKPHA__) == SPI_CLKPHASE_HIGH))
#define IS_SPI_NSS(__NSS__)            (((__NSS__) == SPI_NSS_SOFT) || ((__NSS__) == SPI_NSS_HARD))
#define IS_SPI_MLSB(__BIT__)           (((__BIT__) == SPI_MSB_FIRSTBIT) || ((__BIT__) == SPI_LSB_FIRSTBIT))

#define IS_SPI_DMA_HANDLE(__INSTANCE__)  ((__INSTANCE__) != NULL)


/**
  * @brief  Make specified SPI active.
  * @param  SPIx        SPI instance.
  * @retval None.
  */
void DDL_SPI_Instance_Active(SPI_Type *SPIx);

/**
  * @brief  Make specified SPI deactive.
  * @param  SPIx        SPI instance.
  * @retval None.
  */
void DDL_SPI_Instance_Deactive(SPI_Type *SPIx);

/**
  * @brief  Configure the elements of structure SPI_Init_T to default values.
  * @param  pSPIStruct Pointer to a SPI_Init_T structure that contains
  *         the configuration information for the given SPI module.
  * @retval None
  */
void DDL_SPI_StructInit(SPI_Init_T *pSPIStruct);

/**
  * @brief  Initializes the SPI.
  * @param  SPIx   SPI instance.
  * @param  pSPIStruct Pointer to a SPI_Init_T structure that contains
  *         the configuration information for the given SPI module.
  * @retval DDL status
  */
DDL_Status_T DDL_SPI_Init(SPI_Type *SPIx, SPI_Init_T *pSPIStruct);

/**
  * @brief  SPI as master or slave transmits an amount of data through polling mode.
  * @param  SPIx     SPI instance.
  * @param  pData    pointer to data buffer.
  * @param  size     the amount of data to be sent.
  * @param  timeout  timeout duration.
  * @retval DDL status
  */
DDL_Status_T DDL_SPI_Transmit(SPI_Type *SPIx, uint8_t *pData, uint16_t size, uint32_t timeout);

/**
  * @brief  SPI as master to receives an amount of data through polling mode.
  * @param  SPIx     SPI instance.
  * @param  pData    pointer to data buffer.
  * @param  size     the amount of data to be sent.
  * @param  timeout  timeout duration.
  * @retval DDL status
  */
DDL_Status_T DDL_SPI_MasterReceive(SPI_Type *SPIx, uint8_t *pData, uint16_t size, uint32_t timeout);

/**
  * @brief  SPI as slave to receives an amount of data through polling mode.
  * @param  SPIx     SPI instance.
  * @param  pData    pointer to data buffer.
  * @param  size     the amount of data to be sent.
  * @param  timeout  timeout duration.
  * @retval DDL status
  */
DDL_Status_T DDL_SPI_SlaveReceive(SPI_Type *SPIx, uint8_t *pData, uint16_t size, uint32_t timeout);

/**
  * @brief  SPI as master or slave (Full-Duplex) transmits and receives
  *         an amount of data through polling mode.
  * @param  SPIx   SPI instance.
  * @param  pData  pointer to data buffer.
  * @param  size   the amount of data to be sent and receive.
  * @param  timeout  timeout duration.
  * @retval DDL status
  */
DDL_Status_T DDL_SPI_TransmitReceive(SPI_Type *SPIx, uint8_t *pTxData, uint8_t *pRxData, uint16_t size, uint32_t timeout);

/**
  * @brief  Enable SPI interrupt with specified type.
  * @param  SPIx     SPI instance.
  * @param  intrMask SPI interrupt type.
  * @retval None
  */
void DDL_SPI_IntEnable(SPI_Type *SPIx, uint32_t intrMask);

/**
  * @brief  Disable SPI interrupt with specified type.
  * @param  SPIx     SPI instance.
  * @param  intrMask SPI interrupt type.
  * @retval None
  */
void DDL_SPI_IntDisable(SPI_Type *SPIx, uint32_t intrMask);

/**
  * @brief  Enable SPI TX DMA with specified trigger level.
  * @param  SPIx      SPI instance.
  * @param  trigLevel transmit DMA trigger level.
  * @retval None
  */
void DDL_SPI_TXDMAEnable(SPI_Type *SPIx, uint8_t trigLevel);

/**
  * @brief  Enable SPI RX DMA with specified trigger level.
  * @param  SPIx      SPI instance.
  * @param  trigLevel receive DMA trigger level.
  * @retval None
  */
void DDL_SPI_RXDMAEnable(SPI_Type *SPIx, uint8_t trigLevel);


#ifdef __cplusplus
}
#endif

#endif /* __KPM32xx_DDL_SPI_H */
