#include "ldc1612.h"

#define I2C_TIMEOUT_MS 50U

static volatile uint32_t spurious_berr_count;

/*
 * STM32G0B1 errata ES0548, section 2.10.2:
 * a master transfer may raise BERR spuriously although it continues normally.
 * The prescribed workaround is to clear BERR and take no other action.
 */
void I2C1_IRQHandler(void)
{
  uint32_t status = I2C1->ISR;

  if ((status & I2C_ISR_BERR) != 0U) {
    I2C1->ICR = I2C_ICR_BERRCF;
    ++spurious_berr_count;
  }

  /*
   * Do not consume real arbitration or overrun errors here. Disabling their
   * interrupt lets the blocking HAL call detect and report them normally.
   */
  if ((status & (I2C_ISR_ARLO | I2C_ISR_OVR)) != 0U) {
    CLEAR_BIT(I2C1->CR1, I2C_CR1_ERRIE);
  }
}

static void enable_berr_workaround(I2C_HandleTypeDef *i2c)
{
  if (i2c->Instance == I2C1) {
    __HAL_I2C_ENABLE_IT(i2c, I2C_IT_ERRI);
  }
}

HAL_StatusTypeDef ldc1612_write_register(
    I2C_HandleTypeDef *i2c,
    uint8_t reg,
    uint16_t value)
{
  /* Data sheet section 7.5: pointer, high byte, low byte, then STOP. */
  uint8_t bytes[3] = {
      reg,
      (uint8_t)(value >> 8),
      (uint8_t)value,
  };

  enable_berr_workaround(i2c);
  return HAL_I2C_Master_Transmit(
      i2c,
      LDC1612_ADDRESS << 1,
      bytes,
      sizeof(bytes),
      I2C_TIMEOUT_MS);
}

HAL_StatusTypeDef ldc1612_read_register(
    I2C_HandleTypeDef *i2c,
    uint8_t reg,
    uint16_t *value)
{
  uint8_t bytes[2] = {0U, 0U};

  /*
   * HAL_I2C_Mem_Read generates the required pointer write, repeated START,
   * two read bytes, and STOP.
   */
  enable_berr_workaround(i2c);
  HAL_StatusTypeDef status = HAL_I2C_Mem_Read(
      i2c,
      LDC1612_ADDRESS << 1,
      reg,
      I2C_MEMADD_SIZE_8BIT,
      bytes,
      sizeof(bytes),
      I2C_TIMEOUT_MS);

  if (status == HAL_OK) {
    *value = ((uint16_t)bytes[0] << 8) | bytes[1];
  }
  return status;
}

uint32_t ldc1612_spurious_berr_count(void)
{
  return spurious_berr_count;
}
