/*******************************************************************************
 * @file     hv.c
 * @brief    High voltage module firmware functions.
 * @version  V4.0.1
 * @date     30-July-2026
 *
 * @note
 * Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd.. All rights reserved.
 *
 * @attention
 * THIS SOFTWARE JUST PROVIDES CUSTOMERS WITH CODING INFORMATION REGARDING
 * THEIR PRODUCTS, WHICH AIMS AT SAVING TIME FOR THEM. SPINTROL SHALL NOT BE
 * LIABLE FOR THE USE OF THE SOFTWARE. SPINTROL DOES NOT GUARANTEE THE
 * CORRECTNESS OF THIS SOFTWARE AND RESERVES THE RIGHT TO MODIFY THE SOFTWARE
 * WITHOUT NOTIFICATION.
 *
 ******************************************************************************/


#include "spd1188_hv.h"


#if defined(__CC_ARM)
    #pragma push
    #pragma O1
#endif




/********************************************************************************
 * @brief      Initialize High Voltage Module Pin Interface
 *
 * @param[in]  none
 *
 * @return     none
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void PREDRIVER_PinInit(void)
{
  /* Pin for High Voltage Module */
  GPIO_SetPinChannel(GPIO_HV_PWM_W_L, GPIO24_PWM3A);
  GPIO_SetPinChannel(GPIO_HV_PWM_W_H, GPIO25_PWM3B);

  GPIO_SetPinChannel(GPIO_HV_PWM_V_L, GPIO26_PWM4A);
  GPIO_SetPinChannel(GPIO_HV_PWM_V_H, GPIO27_PWM4B);

  GPIO_SetPinChannel(GPIO_HV_PWM_U_L, GPIO28_PWM5A);
  GPIO_SetPinChannel(GPIO_HV_PWM_U_H, GPIO29_PWM5B);
}




/********************************************************************************
 * @brief      Initialize High Voltage Module Communication Interface
 *
 * @param[in]  u32Baudrate:  Communication Speed (bps)
 *
 * @return     none
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_ComInit(uint32_t u32Baudrate)
{
  /*  Enable Clock for SSP */
  CLOCK_EnableModule(SSP_MODULE);

  /* Init SSP - Master Mode, 32-bit frame size */
  SSP_Init(SSP, SSP_MASTER_MODE, 32, u32Baudrate, SSP_FRM_ACTIVE_LOW, SSP_CLK_IDLE_LOW, SSP_SEND_ON_FALLING_EDGE);

  /* Enable SSP */
  SSP_Enable(SSP);

  /* Enable High Voltage Module Communication Interface */
  EPWR_WALLOW();
  EPWR_Enable();
  EPWR_EnableComInterface();
  EPWR_WDIS();

  return SUCCESS;
}




/********************************************************************************
 * @brief      Initialize High Voltage Module
 *
 * @param[in]  u32Baudrate:  Communication Speed (bps)
 * @param[in]  pu8ID      :  Pointer to the data stored High Voltage Module ID
 * @param[in]  u32Timeout :  Time-out
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_Init(uint32_t u32Baudrate, uint8_t *pu8ID, uint32_t u32Timeout)
{
  /* Init Pin Interface */
  PREDRIVER_PinInit();

  /* Init Communication */
  HV_ComInit(u32Baudrate);

  /* Handshake with High Voltage Module */
  if(HV_Handshake(pu8ID, u32Timeout) == ERROR)
  {
    return ERROR;
  }

  /* Init High Voltage Module registers */
  return HV_InitReg();
}




/********************************************************************************
 * @brief      Send command and receive its response
 *
 * @param[in]  u32Req  :  Request data
 * @param[in]  pu32Resp:  Pointer to the data received
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_Command(uint32_t u32Req, uint32_t *pu32Resp)
{
  /* Init the count number */
  volatile uint32_t u32Timeout;
    
  /* Set High Voltage Module Communication Data Pin Direction - Transmit data */
  EPWR_WALLOW();
  EPWR_EnableTransmitData();
  EPWR_WDIS();

  /* Init the count number */
  u32Timeout = 0U;
  
  /* Wait until SSP TxFIFO is Not Full */
  while((SSP_IsTxNotFull(SSP) == 0U) && (u32Timeout < HV_TIMEOUT_MAX))
  {
    u32Timeout++;
  }
  /* Timeout */
  if(u32Timeout >= HV_TIMEOUT_MAX)
  {
    return ERROR;
  }

  /* Send Request data to High Voltage Module */
  SSP_SendData(SSP, u32Req);

  /* Init the count number */
  u32Timeout = 0U;
  
  /* Wait until SSP RxFIFO is Not Empty */
  while((SSP_IsRxNotEmpty(SSP) == 0U) && (u32Timeout < HV_TIMEOUT_MAX))
  {
    u32Timeout++;
  }
  /* Timeout */
  if(u32Timeout >= HV_TIMEOUT_MAX)
  {
    return ERROR;
  }

  /* Get SSP receive data from buffer, ignore these data */
  while(SSP_IsRxNotEmpty(SSP))
  {
    SSP_ReceiveData(SSP);
  }

  /* This delay is important for correct data transfer */
  Delay_Us(10);

  /* Set High Voltage Module Communication Data Pin Direction - Receive data */
  EPWR_WALLOW();
  EPWR_EnableReceiveData();
  EPWR_WDIS();

  /* Send dummy data for receive data from High Voltage Module */
  SSP_SendData(SSP, 0);

  /* Init the count number */
  u32Timeout = 0U;
  
  /* Wait until SSP RxFIFO is not empty */
  while((SSP_IsRxNotEmpty(SSP) == 0U) && (u32Timeout < HV_TIMEOUT_MAX))
  {
    u32Timeout++;
  }
  /* Timeout */
  if(u32Timeout >= HV_TIMEOUT_MAX)
  {
    return ERROR;
  }

  /* Get Response Data From High Voltage Module */
  *pu32Resp = SSP_ReceiveData(SSP);

  return SUCCESS;
}




/********************************************************************************
 * @brief      Handshake with High Voltage Module
 *
 * @param[in]  pu8ID     :  Pointer to the data stored ID value
 * @param[in]  u32Timeout:  Time-out
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_Handshake(uint8_t *pu8ID, uint32_t u32Timeout)
{
  uint32_t u32RxData = 0;
  volatile uint32_t u32Count = 0;

  /* Handshake Protocol */
  while(1)
  {
    Delay_Us(100);

    /* Try to Get High Voltage Module ID */
    if(HV_Command(HV_CMD_READ_ID, &u32RxData) == ERROR)
    {
      return ERROR;
    }

    /* Validate received data */
    if((u32RxData & 0xFFFFFF00U) == HV_RESP_READ_ID)
    {
      /* Valid ID data */
      if(pu8ID != NULL) *pu8ID = (uint8_t)(u32RxData & 0x000000FFU);

      return SUCCESS;
    }
    else if(u32Count++ > u32Timeout)
    {
      return ERROR;
    }
  }
}




/********************************************************************************
 * @brief      Write data to High Voltage Module register
 *
 * @param[in]  u8Addr:  Register address
 * @param[in]  u8Data:  Data to be written to the specified register
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_WriteReg(uint8_t u8Addr, uint8_t u8Data)
{
  uint32_t u32Request = 0;
  uint32_t u32Response = 0;

  /* Check register address */
  if(!IS_HV_REG(u8Addr))
  {
    return ERROR;
  }

  /* Write request */
  u32Request = HV_CMD_WRITE | ((u8Addr & 0x0000001FU) << 16) | ((u8Data & 0x000000FFU) <<  8);

  /* Send write request data */
  if(HV_Command(u32Request, &u32Response) == ERROR)
  {
    return ERROR;
  }

  /* Validate response data */
  if(u32Response != (HV_RESP_WRITE | ((u8Addr & 0x0000001FU) << 16) | ((u8Data & 0x000000FFU) <<  8)))
  {
    return ERROR;
  }

  /* Excute request */
  u32Request = HV_CMD_EXECUTE;

  /* Send excute request data */
  if(HV_Command(u32Request, &u32Response) == ERROR)
  {
    return ERROR;
  }

  /* Validate response data */
  if(u32Response != HV_RESP_EXECUTE)
  {
    return ERROR;
  }

  return SUCCESS;
}




/********************************************************************************
 * @brief      Read data from High Voltage Module register
 *
 * @param[in]  u8Addr :  Register address
 * @param[in]  pu8Data:  Pointer to the data readed from register
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_ReadReg(uint8_t u8Addr, uint8_t *pu8Data)
{
  ErrorStatus eStatus = ERROR;
  uint32_t u32Request = 0;
  uint32_t u32Response = 0;

  /* Check register address */
  if(!IS_HV_REG(u8Addr))
  {
    return ERROR;
  }

  /* Read request */
  u32Request = HV_CMD_READ | (((u8Addr) & 0x0000001FU) << 16);

  /* Send read request data */
  eStatus = HV_Command(u32Request, &u32Response);
  /* Get Register value */
  *pu8Data = (uint8_t)(u32Response & 0x000000FFU);

  if(ERROR == eStatus)
  {
    return ERROR;
  }

  /* Validate response data */
  if((u32Response & 0xFFFF0000U) != (HV_RESP_READ | (((u8Addr) & 0x0000001FU) << 16)))
  {
    return ERROR;
  }

  /* Validate register data */
  if((u32Response & 0x000000FFU) != ((u32Response & 0x0000FF00U) >> 8))
  {
    return ERROR;
  }

  return SUCCESS;
}




/********************************************************************************
 * @brief      Write High Voltage Module register bit-fieds
 *
 * @param[in]  u8Addr      :  Register address
 * @param[in]  u8Mask      :  Bit-field mask
 * @param[in]  u8FieldData :  Register bit-field value (shifted according to its position)
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_WriteRegField(uint8_t u8Addr, uint8_t u8Mask, uint8_t u8FieldData)
{
  uint8_t u8RxData = 0;

  /* Check register address */
  if(!IS_HV_REG(u8Addr))
  {
    return ERROR;
  }

  /* Read register data */
  if(HV_ReadReg(u8Addr, &u8RxData) == ERROR)
  {
    return ERROR;
  }

  /* Current register bit value == setting data */
  if((u8RxData & (u8Mask)) == (u8FieldData))
  {
    return SUCCESS;
  }

  u8RxData &= (~(u8Mask));
  u8RxData |= (u8FieldData);

  return HV_WriteReg(u8Addr, u8RxData);
}




/********************************************************************************
 * @brief      Init High Voltage Module Registers (Trim)
 *
 * @param[in]  none
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_InitReg(void)
{
  /* Read Trim value from Flash and Set High Voltage Module Registers */
  uint32_t u32Temp;
  
  /* Get Trim Flag */
  u32Temp = *(__IO uint32_t *)(0x11000200 + 0x38 * 4);
  
  /* Valid Trim Flag */
  if(u32Temp == 0x476F6F64)
  {
    /* Enable Write Access */
    if(HV_WriteReg(HV_REG_CTLKEY, CTLKEY_ALL_KEY_ENGR) == ERROR)
    {
      return ERROR;
    }
    
    /* Write LDOTRIM value */
    u32Temp = *(__IO uint32_t *)(0x11000200 + 0x39 * 4);
    if(HV_WriteReg(0x13, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Write RCOTRIM value */
    u32Temp = *(__IO uint32_t *)(0x11000200 + 0x3A * 4);
    if(HV_WriteReg(0x14, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Write BODREFTRIM value */
    u32Temp = *(__IO uint32_t *)(0x11000200 + 0x3B * 4);
    if(HV_WriteReg(0x15, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Write BUCK3TRIM[REFCODE] value */
    u32Temp = *(__IO uint32_t *)(0x11000200 + 0x3C * 4);
    u32Temp &= 0x7F;
    if(HV_WriteReg(0x16, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Write BUCK3TRIM[OCTHCODE] value */
    u32Temp |= 0x80;
    if(HV_WriteReg(0x16, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Write TONTRIM[BUCK3] value */
    u32Temp = *(__IO uint32_t *)(0x11000200 + 0x3D * 4);
    u32Temp &= 0x7F;
    if(HV_WriteReg(0x17, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Write TONTRIM[BUCKG] value */
    u32Temp |= 0x80;
    if(HV_WriteReg(0x17, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Write BUCKGENGR2 value */
    u32Temp = *(__IO uint32_t *)(0x11000200 + 0x3E * 4);
    if(HV_WriteReg(0x1A, u32Temp) == ERROR)
    {
      return ERROR;
    }

    if(HV_WriteReg(0x18, 0x02) == ERROR)
    {
      return ERROR;
    }

    /* Disable Write Access */
    if(HV_WriteReg(HV_REG_CTLKEY, 0x00) == ERROR)
    {
      return ERROR;
    }
  }
  else
  {
    return ERROR;
  }

  /* Set Pre-driver over-current monitor blanking, filtering window to 0.4us */
  if(HV_WriteRegField(HV_REG_PDRVOCCTL, (0x3 << 6) | (0x3 << 4), (0x0 << 6) | (0x0 << 4)) == ERROR)
  {
    return ERROR;
  }

  return SUCCESS;
}




/********************************************************************************
 * @brief      Reset High Voltage Module Registers
 *
 * @param[in]  none
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_ResetReg(void)
{
  uint32_t u32Request = 0;
  uint32_t u32Response = 0;

  /* Allow High Voltage Module to response to software-reset command */
  if(HV_WriteReg(HV_REG_CTLKEY, CTLKEY_ALL_KEY_SRSTALL) == ERROR)
  {
    return ERROR;
  }

  /* Excute request */
  u32Request = HV_CMD_RESET;

  /* Send excute request data */
  if(HV_Command(u32Request, &u32Response) == ERROR)
  {
    return ERROR;
  }

  /* Validate response data */
  if(u32Response != HV_RESP_RESET)
  {
    return ERROR;
  }
  
  return SUCCESS;
}




/********************************************************************************
 * @brief      Set High Voltage Module into Deep Sleep Mode
 *
 * @param[in]  none
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_DeepSleep(void)
{
  uint32_t u32Request = 0;
  uint32_t u32Response = 0;

  /* Allow High Voltage Module to response to deep-sleep command */
  if(HV_WriteReg(HV_REG_CTLKEY, CTLKEY_ALL_KEY_DPSLP) == ERROR)
  {
    return ERROR;
  }

  /* Excute request */
  u32Request = HV_CMD_DEEP_SLEEP;

  /* Send excute request data */
  if(HV_Command(u32Request, &u32Response) == ERROR)
  {
    return ERROR;
  }

  return SUCCESS;
}




/********************************************************************************
 * @brief      Connect High Voltage Module ATEST signal to ADC ATEST channel input
 *
 * @param[in]  none
 *
 * @return     none
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void HV_ConnectAnalogTestToADC(void)
{
  uint32_t u32Temp;
  __IO uint32_t *pREG = (uint32_t *)(0x40000F50);
  
  *pREG = 0x1ACCE551;
  
  pREG = (uint32_t *)(0x40000F00);
  u32Temp = *pREG;
  u32Temp |= 0x1000;
  *pREG = u32Temp;
  
  pREG = (uint32_t *)(0x40000F50);
  *pREG = 0;
}




/********************************************************************************
 * @brief      Connect High Voltage Module T-Sensor output to ATEST channel
 *
 * @param[in]  none
 *
 * @return     ERROR or SUCCESS
 *
 * @note       Make sure voltage monitor are disabled firstly!!!
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_ConnectTSensorToAnalogTest(void)
{
  /* Enable Write Access */
  if(HV_WriteReg(HV_REG_CTLKEY, CTLKEY_ALL_KEY_ENGR) == ERROR)
  {
    return ERROR;
  }
  
  
  /* Enable BUCK3BOD analog test */
  if(HV_WriteReg(0x1E, 0x10) == ERROR)
  {
    return ERROR;
  }
  
  /* Select T-Sensor as ATEST signal */
  if(HV_WriteReg(0x1D, 0x70) == ERROR)
  {
    return ERROR;
  }
  
  
  /* Disable Write Access */
  if(HV_WriteReg(HV_REG_CTLKEY, 0x00) == ERROR)
  {
    return ERROR;
  }
  
  return SUCCESS;
}




/********************************************************************************
 * @brief      Set High Voltage Module ChargePump clock divide value
 *
 * @param[in]  eClockDiv      :  Select the ChargePump clock divide value, can goto
 *                               HV_ChargePumpClockDivEnum for more information
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_SetChargePumpClockDiv(HV_ChargePumpClockDivEnum eClockDiv)
{
  /* Enable Write Access */
  if(HV_WriteReg(HV_REG_CTLKEY, CTLKEY_ALL_KEY_ENGR) == ERROR)
  {
    return ERROR;
  }
   
  /* Select the ChargePump clock divide value */
  if(HV_WriteRegField(0x1D, 0xC, eClockDiv << 2) == ERROR)
  {
    return ERROR;
  }
   
  /* Disable Write Access */
  if(HV_WriteReg(HV_REG_CTLKEY, 0x00) == ERROR)
  {
    return ERROR;
  }
  
  return SUCCESS;
}




/********************************************************************************
 * @brief      Get High Voltage Module ChargePump clock divide value
 *
 * @param[in]  pu8Data:  Pointer to the data readed from register, *pu8Data can 
 *                       goto HV_ChargePumpClockDivEnum for more information
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_GetChargePumpClockDiv(uint8_t *pu8Data)
{
  /* Select the ChargePump clock divide value */
  if(HV_ReadReg(0x1D, pu8Data) == ERROR)
  {
    return ERROR;
  }
   
  *pu8Data = ((*pu8Data) & 0xC) >> 2;
  
  return SUCCESS;
}


#if defined(__CC_ARM)
    #pragma pop
#endif


/******************* Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd. ***** END OF FILE ****/
