/**
  **************************************************************************
  * @file     at32f435_437_qspi.c
  * @brief    contain all the functions for qspi firmware library
  **************************************************************************
  *                       Copyright notice & Disclaimer
  *
  * The software Board Support Package (BSP) that is made available to
  * download from Artery official website is the copyrighted work of Artery.
  * Artery authorizes customers to use, copy, and distribute the BSP
  * software and its related documentation for the purpose of design and
  * development in conjunction with Artery microcontrollers. Use of the
  * software is governed by this copyright notice and the following disclaimer.
  *
  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
  *
  **************************************************************************
  */

#include "at32f435_437_conf.h"

/** @addtogroup AT32F435_437_periph_driver
  * @{
  */

/** @defgroup QSPI
  * @brief QSPI driver modules
  * @{
  */

#ifdef QSPI_MODULE_ENABLED

/** @defgroup QSPI_private_functions
  * @{
  */

/**
  * @brief  enable/disable encryption for qspi.
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void qspi_encryption_enable(qspi_type* qspi_x, confirm_state new_state)
{
  qspi_x->ctrl_bit.keyen = new_state;
}

/**
  * @brief  set qspi sck mode.
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_mode: new state to be set
  *         this parameter can be one of the following values:
  *         - QSPI_SCK_MODE_0
  *         - QSPI_SCK_MODE_3
  * @retval none
  */
void qspi_sck_mode_set(qspi_type* qspi_x, qspi_clk_mode_type new_mode)
{
  qspi_x->ctrl_bit.sckmode = new_mode;
}

/**
  * @brief  set qspi clock division
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_clkdiv: new division value
  *         this parameter can be one of the following values:
  *         - QSPI_CLK_DIV_2
  *         - QSPI_CLK_DIV_4
  *         - QSPI_CLK_DIV_6
  *         - QSPI_CLK_DIV_8
  *         - QSPI_CLK_DIV_3
  *         - QSPI_CLK_DIV_5
  *         - QSPI_CLK_DIV_10
  *         - QSPI_CLK_DIV_12
  * @retval none
  */
void qspi_clk_division_set(qspi_type* qspi_x, qspi_clk_div_type new_clkdiv)
{
  qspi_x->ctrl_bit.clkdiv = new_clkdiv;
}

/**
  * @brief  enable/disable cache in xip mode
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void qspi_xip_cache_bypass_set(qspi_type* qspi_x, confirm_state new_state)
{
  qspi_x->xip_cmd_w3_bit.bypassc = new_state;
}

/**
  * @brief  enable/disable interrupt when command is completed
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void qspi_interrupt_enable(qspi_type* qspi_x, confirm_state new_state)
{
  qspi_x->ctrl2_bit.cmdie = new_state;
}

/**
  * @brief  get status flags.
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  flag: specifies the flag to check.
  *         this parameter can be one of the following values:
  *         - QSPI_RXFIFORDY_FLAG
  *         - QSPI_TXFIFORDY_FLAG
  *         - QSPI_CMDSTS_FLAG
  * @retval the new state of usart_flag (SET or RESET).
  */
flag_status qspi_flag_get(qspi_type* qspi_x, uint32_t flag)
{
  flag_status bit_status = RESET;
  switch(flag)
  {
    case QSPI_RXFIFORDY_FLAG:
      bit_status = (flag_status)qspi_x->fifosts_bit.rxfifordy;
      break;
    case QSPI_TXFIFORDY_FLAG:
      bit_status = (flag_status)qspi_x->fifosts_bit.txfifordy;
      break;
    case QSPI_CMDSTS_FLAG:
      bit_status = (flag_status)qspi_x->cmdsts_bit.cmdsts;
      break;
    default:
      break;
  }
  return bit_status;
}

/**
  * @brief  clear flags
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  flag: flags to be clear
  *         this parameter can be one of the following values:
  *         - QSPI_CMDSTS_FLAG
  * @retval none
  */
void qspi_flag_clear(qspi_type* qspi_x, uint32_t flag)
{
  (void)flag;
  qspi_x->cmdsts = QSPI_CMDSTS_FLAG;
}

/**
  * @brief  set dma threshold for dma rx
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_threshold: value to set
  *         this parameter can be one of the following values:
  *         - QSPI_DMA_FIFO_THOD_WORD08
  *         - QSPI_DMA_FIFO_THOD_WORD16
  *         - QSPI_DMA_FIFO_THOD_WORD32
  * @retval none
  */
void qspi_dma_rx_threshold_set(qspi_type* qspi_x, qspi_dma_fifo_thod_type new_threshold)
{
  qspi_x->ctrl2_bit.rxfifo_thod = new_threshold;
}

/**
  * @brief  set dma threshold for dma tx
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_threshold: value to set
  *         this parameter can be one of the following values:
  *         - QSPI_DMA_FIFO_THOD_WORD08
  *         - QSPI_DMA_FIFO_THOD_WORD16
  *         - QSPI_DMA_FIFO_THOD_WORD32
  * @retval none
  */
void qspi_dma_tx_threshold_set(qspi_type* qspi_x, qspi_dma_fifo_thod_type new_threshold)
{
  qspi_x->ctrl2_bit.txfifo_thod = new_threshold;
}

/**
  * @brief  enable/disable dma transfer
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void qspi_dma_enable(qspi_type* qspi_x, confirm_state new_state)
{
  qspi_x->ctrl2_bit.dmaen = new_state;
}

/**
  * @brief  set wip position in status register of flash
  * @note   the function must be configured only when qspi in command-port mode!!!
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  busy_pos: value to set
  *         this parameter can be one of the following values:
  *         - QSPI_BUSY_OFFSET_0
  *         - QSPI_BUSY_OFFSET_1
  *         - QSPI_BUSY_OFFSET_2
  *         - QSPI_BUSY_OFFSET_3
  *         - QSPI_BUSY_OFFSET_4
  *         - QSPI_BUSY_OFFSET_5
  *         - QSPI_BUSY_OFFSET_6
  *         - QSPI_BUSY_OFFSET_7
  * @retval none
  */
void qspi_busy_config(qspi_type* qspi_x, qspi_busy_pos_type busy_pos)
{
  qspi_x->ctrl_bit.busy = busy_pos;
}

/**
  * @brief  enable xip mode or command-port mode.
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  new_state (TRUE or FALSE)
  * @retval none
  */
void qspi_xip_enable(qspi_type* qspi_x, confirm_state new_state)
{
  /* skip if state is no change */
  if(new_state == (confirm_state)(qspi_x->ctrl_bit.xipsel))
  {
    return;
  }

  /* wait until tx fifo emoty*/
  while(qspi_x->fifosts_bit.txfifordy == 0);

  /* flush and reset qspi state */
  qspi_x->ctrl_bit.xiprcmdf = 1;

  /* wait until action is finished */
  while(qspi_x->ctrl_bit.abort);

  /* set xip mode to new state */
  qspi_x->ctrl_bit.xipsel = new_state;

  /* wait until abort is not set */
  while(qspi_x->ctrl_bit.abort);

  /* wait until cache status valid*/
  if(new_state == TRUE)
  {
    while( qspi_x->xip_cmd_w3_bit.csts );
  }
}

/**
  * @brief  set command-port and qspi_x will start to work
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  qspi_cmd_struct: pointer to qspi cmd structure
  * @retval none
  */
void qspi_cmd_operation_kick(qspi_type* qspi_x, qspi_cmd_type* qspi_cmd_struct)
{
  uint32_t w1_val = 0, w3_val = 0;

  /* config analyse cmd_w0 register */
  qspi_x->cmd_w0 = (uint32_t)qspi_cmd_struct->address_code;

  /* config analyse cmd_w1 register */
  w1_val = (uint32_t)qspi_cmd_struct->address_length;
  w1_val |= (uint32_t)(qspi_cmd_struct->second_dummy_cycle_num << 16);
  w1_val |= (uint32_t)(qspi_cmd_struct->instruction_length << 24);
  w1_val |= (uint32_t)(qspi_cmd_struct->pe_mode_enable << 28);
  qspi_x->cmd_w1 = w1_val;

  /* config analyse cmd_w2 register */
  qspi_x->cmd_w2 = (uint32_t)qspi_cmd_struct->data_counter;

  /* config analyse cmd_w3 register */
  w3_val = (uint32_t)(qspi_cmd_struct->write_data_enable << 1);
  w3_val |= (uint32_t)(qspi_cmd_struct->read_status_enable << 2);
  w3_val |= (uint32_t)(qspi_cmd_struct->read_status_config << 3);
  w3_val |= (uint32_t)(qspi_cmd_struct->operation_mode << 5);
  w3_val |= (uint32_t)(qspi_cmd_struct->pe_mode_operate_code << 16);
  w3_val |= (uint32_t)(qspi_cmd_struct->instruction_code << 24);
  qspi_x->cmd_w3 = w3_val;
}

/**
  * @brief  initial xip mode for qspi_x
  * @param  qspi_x: select the qspi peripheral.
  *         this parameter can be one of the following values:
  *         QSPI1,QSPI2.
  * @param  xip_init_struct: pointer to xip init structure.
  * @retval none.
  */
void qspi_xip_init(qspi_type* qspi_x, qspi_xip_type* xip_init_struct)
{
  uint32_t xc0_val = 0, xc1_val = 0, xc2_val = 0;
  /* config analyse xip_cmd_w0 register */
  xc0_val = (uint32_t)xip_init_struct->read_second_dummy_cycle_num;
  xc0_val |= (uint32_t)(xip_init_struct->read_operation_mode << 8);
  xc0_val |= (uint32_t)(xip_init_struct->read_address_length << 11);
  xc0_val |= (uint32_t)(xip_init_struct->read_instruction_code << 12);
  qspi_x->xip_cmd_w0 = xc0_val;

  /* config analyse xip_cmd_w1 register */
  xc1_val = (uint32_t)xip_init_struct->write_second_dummy_cycle_num;
  xc1_val |= (uint32_t)(xip_init_struct->write_operation_mode << 8);
  xc1_val |= (uint32_t)(xip_init_struct->write_address_length << 11);
  xc1_val |= (uint32_t)(xip_init_struct->write_instruction_code << 12);
  qspi_x->xip_cmd_w1 = xc1_val;

  /* config analyse xip_cmd_w2 register */
  xc2_val = (uint32_t)xip_init_struct->read_data_counter;
  xc2_val |= (uint32_t)(xip_init_struct->read_time_counter << 8);
  xc2_val |= (uint32_t)(xip_init_struct->read_select_mode << 15);
  xc2_val |= (uint32_t)(xip_init_struct->write_data_counter << 16);
  xc2_val |= (uint32_t)(xip_init_struct->write_time_counter << 24);
  xc2_val |= (uint32_t)(xip_init_struct->write_select_mode << 31);
  qspi_x->xip_cmd_w2 = xc2_val;
}

/**
  * @brief  read one byte from qspi device in command mode
  * @param  qspi_x: select the qspi peripheral.
  * @retval 8-bit data.
  */
uint8_t qspi_byte_read(qspi_type* qspi_x)
{
  return qspi_x->dt_u8;
}

/**
  * @brief  read one half-word from qspi device in command mode
  * @param  qspi_x: select the qspi peripheral.
  * @retval 16-bit data.
  */
uint16_t qspi_half_word_read(qspi_type* qspi_x)
{
  return qspi_x->dt_u16;
}

/**
  * @brief  read one word from qspi device in command mode
  * @param  qspi_x: select the qspi peripheral.
  * @retval 32-bit data.
  */
uint32_t qspi_word_read(qspi_type* qspi_x)
{
  return qspi_x->dt;
}

/**
  * @brief  write one byte to qspi device in command mode
  * @param  qspi_x: select the qspi peripheral.
  * @param  value:  8-bit data.
  * @retval none.
  */
void qspi_byte_write(qspi_type* qspi_x, uint8_t value)
{
  qspi_x->dt_u8 = value;
}

/**
  * @brief  write one half-word to qspi device in command mode
  * @param  qspi_x: select the qspi peripheral.
  * @param  value:  16-bit data.
  * @retval none.
  */
void qspi_half_word_write(qspi_type* qspi_x, uint16_t value)
{
  qspi_x->dt_u16 = value;
}

/**
  * @brief  write one word to qspi device in command mode
  * @param  qspi_x: select the qspi peripheral.
  * @param  value:  32-bit data.
  * @retval none.
  */
void qspi_word_write(qspi_type* qspi_x, uint32_t value)
{
  qspi_x->dt = value;
}

/**
  * @}
  */

#endif

/**
  * @}
  */

/**
  * @}
  */
