/*******************************************************************************
 * @file     hv.c
 * @brief    High voltage module firmware functions.
 * @version  V4.0.3
 * @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 "spd1178_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 Pre-driver 0 */
  GPIO_SetPinChannel(GPIO_HV_PWM_U0_L, GPIO22_PWM2A);
  GPIO_SetPinChannel(GPIO_HV_PWM_U0_H, GPIO23_PWM2B);

  GPIO_SetPinChannel(GPIO_HV_PWM_V0_L, GPIO20_PWM1A);
  GPIO_SetPinChannel(GPIO_HV_PWM_V0_H, GPIO21_PWM1B);

  GPIO_SetPinChannel(GPIO_HV_PWM_W0_L, GPIO18_PWM0A);
  GPIO_SetPinChannel(GPIO_HV_PWM_W0_H, GPIO19_PWM0B);

  /* Pin for Pre-driver 1 */
  GPIO_SetPinChannel(GPIO_HV_PWM_U1_L, GPIO28_PWM5A);
  GPIO_SetPinChannel(GPIO_HV_PWM_U1_H, GPIO29_PWM5B);

  GPIO_SetPinChannel(GPIO_HV_PWM_V1_L, GPIO26_PWM4A);
  GPIO_SetPinChannel(GPIO_HV_PWM_V1_H, GPIO27_PWM4B);

  GPIO_SetPinChannel(GPIO_HV_PWM_W1_L, GPIO24_PWM3A);
  GPIO_SetPinChannel(GPIO_HV_PWM_W1_H, GPIO25_PWM3B);
}




/********************************************************************************
 * @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-fields
 *
 * @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      Enable write access to the High Voltage Module registers
 *
 * @param[in]  none
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_WALLOW(void)
{
  return HV_WriteReg(HV_REG_CTLKEY, HV_WRITE_ACCESS_KEY);
}




/********************************************************************************
 * @brief      Disable write access to the High Voltage Module registers
 *
 * @param[in]  none
 *
 * @return     ERROR or SUCCESS
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_WDIS(void)
{
  return HV_WriteReg(HV_REG_CTLKEY, 0x00);
}




/********************************************************************************
 * @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 + 0x48 * 4);
  
  /* Valid Trim Flag */
  if(u32Temp == 0x476F6F64)
  {
    /* Enable Write Access */
    if(HV_WriteReg(HV_REG_ENGRKEY, HV_WRITE_ACCESS_KEY) == ERROR)
    {
      return ERROR;
    }
    
    /* Write Trim value */
    u32Temp = *(__IO uint32_t *)(0x11000200 + 0x49 * 4);
    if(HV_WriteReg(0x1B, u32Temp) == ERROR)
    {
      return ERROR;
    }
    
    /* Disable Write Access */
    if(HV_WriteReg(HV_REG_ENGRKEY, 0x00) == ERROR)
    {
      return ERROR;
    }
  }
  else
  {
    return ERROR;
  }

  /* Set Pre-driver over-current monitor blanking, filtering window to 0.4us */
  if(HV_WriteRegField(HV_REG_PDRV0CTL1, (0x3 << 0) | (0x3 << 2), (0x0 << 0) | (0x0 << 2)) == 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
 *
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_ConnectTSensorToAnalogTest(void)
{
  /* Enable Write Access */
  if(HV_WriteReg(0x16, 0xAC) == ERROR)
  {
    return ERROR;
  }
  
  
  /* Enable VDDG_LDO analog test */
  if(HV_WriteReg(0x1E, 0x08) == ERROR)
  {
    return ERROR;
  }
  
  /* Select T-Sensor as ATEST signal */
  if(HV_WriteReg(0x1F, 0x01) == ERROR)
  {
    return ERROR;
  }
  
  
  /* Disable Write Access */
  if(HV_WriteReg(0x16, 0x00) == ERROR)
  {
    return ERROR;
  }
  
  return SUCCESS;
}




/********************************************************************************
 * @brief      Enable Pre-Driver Low Voltage Operation
 *
 * @param[in]  none
 *
 * @return     ERROR or SUCCESS
 *
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus HV_EnableLowVoltageOperation(void)
{
  /* Enable Write Access */
  if(HV_WriteReg(0x16, 0xAC) == ERROR)
  {
    return ERROR;
  }
  
  /* Enable the PREDRIVER Low Voltage Operation */
  if(HV_WriteReg(0x19, 0x06) == ERROR)
  {
    return ERROR;
  }
  
  /* Disable Write Access */
  if(HV_WriteReg(0x16, 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(0x16, HV_WRITE_ACCESS_KEY) == ERROR)
    {
        return ERROR;
    }

    /* Select the ChargePump clock divide value */
    if (HV_WriteRegField(0x18, 0x3, eClockDiv) == 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(0x18, pu8Data) == ERROR)
    {
        return ERROR;
    }
    
    *pu8Data = ((*pu8Data) & 0x3);
    
    return SUCCESS;
}


#if defined(__CC_ARM)
    #pragma pop
#endif


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