/*
 * @File name    : driver_spi.c
 * @Author       : Bluetrum IOT Team
 * @Date         : 2023-02-15
 * @Description  : This file provides functions to manage the most functionalities
 *                 of the SPI peripheral.
 *
 * Copyright (c) by Bluetrum, All Rights Reserved.
 */
#include "driver_spi.h"

/**
  * @brief  Initializes the spix peripheral according to the specified
  *         parameters in the spi_init_struct.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  spi_init_struct: pointer to a spi_init_typedef structure that
  *         contains the configuration information for the specified SPI peripheral.
  * @retval None
  */
void spi_init(spi_typedef* spix, spi_init_typedef* spi_init_struct)
{
    uint32_t temp = 0;

    /* SPI_CLK freq configuration */
    spix->baud = sys_clk_nhz_get() / spi_init_struct->baud - 1;

    temp |= (uint32_t)(spi_init_struct->bus_mode & SPIxCON_BUSMODE);
    temp |= (uint32_t)(spi_init_struct->role_mode & SPIxCON_SPISM);
    temp |= (uint32_t)(spi_init_struct->output_data_edge & SPIxCON_SMPS);
    temp |= (uint32_t)(spi_init_struct->sample_clk_edge & SPIxCON_SPIOSS);
    temp |= (uint32_t)(spi_init_struct->clkids & SPIxCON_CLKIDS);

    spix->con = temp;
}

/**
 * @brief  De-initialize the specified SPI peripheral.
 * @param  spix: where x can be (0..1) to select the SPI peripheral.
 * @retval None
 */
void spi_deinit(spi_typedef* spix)
{
    if (spix == SPI0) {
        /* Assert: This release disables disabling SPI0 peripherals using this API */
    } else if (spix == SPI1) {
        spix->con = 0;
        clk_gate1_cmd(CLK_GATE1_SPI1, CLK_DIS);
    }
}

/**
  * @brief  Enable or disable the specified SPI peripheral.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  state: state of the SPIx peripheral.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void spi_cmd(spi_typedef* spix, FUNCTIONAL_STATE state)
{
    if (state) {
        spix->con |= (uint32_t)SPIxCON_SPIEN;
    } else {
        spix->con &= (uint32_t)~SPIxCON_SPIEN;
    }
}

/**
  * @brief  Select the specified SPI direction when in DMA mode or 2-wire mode.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  direction: transmit or receive.
            This parameter can be: SPI_DIR_TX or SPI_DIR_RX.
  * @retval None
  */
void spi_dir_sel(spi_typedef* spix, SPI_DIR_TYPEDEF direction)
{
    if(direction == SPI_DIR_RX) {
        spix->con |= BIT(4);
    } else {
        spix->con &= ~BIT(4);
    }
}

/**
  * @brief  Enable or disable the specified SPI interrupts.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  state: state of the SPIx interrupts.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void spi_it_cmd(spi_typedef* spix, FUNCTIONAL_STATE state)
{
    if (state) {
        spix->con |= (uint32_t)SPIxCON_SPIIE;
    } else {
        spix->con &= (uint32_t)~SPIxCON_SPIIE;
    }
}

/**
  * @brief  Enable or disable the specified SPI LFSR.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  state: state of the SPIx interrupts.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void spi_lfsr_cmd(spi_typedef* spix, FUNCTIONAL_STATE state)
{
    if (state) {
        spix->con |= (uint32_t)SPIxCON_SPILF_EN;
    } else {
        spix->con &= (uint32_t)~SPIxCON_SPILF_EN;
    }
}

/**
  * @brief  Enable or disable the specified SPI multiple bit bus.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  state: state of the SPIx interrupts.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void spi_multi_bit_bus_cmd(spi_typedef* spix, FUNCTIONAL_STATE state)
{
    if (state) {
        spix->con |= (uint32_t)SPIxCON_SPIMBEN;
    } else {
        spix->con &= (uint32_t)~SPIxCON_SPIMBEN;
    }
}

/**
  * @brief  Enable or disable the specified SPI hold when bt rx.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  state: state of the SPIx interrupts.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void spi_hold_rx_cmd(spi_typedef* spix, FUNCTIONAL_STATE state)
{
    if (state) {
        spix->con |= (uint32_t)SPIxCON_HOLDENRX;
    } else {
        spix->con &= (uint32_t)~SPIxCON_HOLDENRX;
    }
}

/**
  * @brief  Enable or disable the specified SPI hold when bt tx.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  state: state of the SPIx interrupts.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void spi_hold_tx_cmd(spi_typedef* spix, FUNCTIONAL_STATE state)
{
    if (state) {
        spix->con |= (uint32_t)SPIxCON_HOLDENTX;
    } else {
        spix->con &= (uint32_t)~SPIxCON_HOLDENTX;
    }
}

/**
  * @brief  Enable or disable the specified SPI software hold.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  state: state of the SPIx interrupts.
            This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void spi_soft_hold_cmd(spi_typedef* spix, FUNCTIONAL_STATE state)
{
    if (state) {
        spix->con |= (uint32_t)SPIxCON_HOLDENSW;
    } else {
        spix->con &= (uint32_t)~SPIxCON_HOLDENSW;
    }
}

/**
  * @brief  Get the SPI pending.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  spi_flag: specifies the flag to set.
  *         this parameter can be one of the following values:
  *             @arg SPI_FLAG_PENDING: SPI(0..1) SPI pending.
  * @retval The state of spi_flag (SET or RESET).
  */
AT(.com_periph.spi.get)
FLAG_STATE spi_get_flag(spi_typedef* spix, uint32_t spi_flag)
{
    if ((spix->con & spi_flag) != RESET) {
        return SET;
    } else {
        return RESET;
    }
}

/**
  * @brief  Clear the SPI pending.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  spi_flag: specifies the flag to clear.
  *         this parameter can be one of the following values:
  *             @arg SPI_FLAG_PENDING: SPI(0..1) SPI pending.
  * @retval None
  */
AT(.com_periph.spi.clear)
void spi_clear_flag(spi_typedef* spix, uint32_t spi_flag)
{
    spix->con |= (uint32_t)spi_flag;
}

/**
  * @brief  Set the SPI counter register value.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  cnt: specifies the counter register new value.
  * @retval None
  */
void spi_set_dma_cnt(spi_typedef* spix, uint32_t cnt)
{
    spix->dma_cnt = cnt;
}

/**
  * @brief  Set the SPI DMA address register value.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  addr: specifies the SPI DMA address register new value.
  * @retval None
  */
void spi_set_dma_addr(spi_typedef* spix, uint32_t addr)
{
    spix->dma_adr = (uint32_t)addr;
}

/**
  * @brief  Get the spi receive data.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @retval spi receive data.
  */
uint8_t spi_receive_data(spi_typedef* spix)
{
    return (uint8_t)spix->buf;
}

/**
  * @brief  Send the spi receive data.
  * @param  spix: where x can be (0..1) to select the SPI peripheral.
  * @param  data: specifies the SPI data to be sent.
  * @retval None.
  */
void spi_send_data(spi_typedef* spix, uint8_t data)
{
    spix->buf = data;
}
