#include "board.h"

I2C_HandleTypeDef hi2c1;

static void system_clock_init(void);
static void ldc_interrupt_init(void);
static void i2c_init(void);

void board_init(void)
{
  HAL_Init();
  system_clock_init();
  ldc_interrupt_init();
  i2c_init();
}

bool board_ldc_interrupt_is_low(void)
{
  /* LDC1612 INTB is push-pull and active low (data sheet section 7.3.3). */
  return HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_6) == GPIO_PIN_RESET;
}

bool board_i2c_scl_is_high(void)
{
  return HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) == GPIO_PIN_SET;
}

bool board_i2c_sda_is_high(void)
{
  return HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET;
}

static void system_clock_init(void)
{
  RCC_OscInitTypeDef oscillator = {0};
  RCC_ClkInitTypeDef clock = {0};

  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  oscillator.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  oscillator.HSIState = RCC_HSI_ON;
  oscillator.HSIDiv = RCC_HSI_DIV1;
  oscillator.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  oscillator.PLL.PLLState = RCC_PLL_ON;
  oscillator.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  oscillator.PLL.PLLM = RCC_PLLM_DIV1;
  oscillator.PLL.PLLN = 8;
  oscillator.PLL.PLLP = RCC_PLLP_DIV2;
  oscillator.PLL.PLLQ = RCC_PLLQ_DIV2;
  oscillator.PLL.PLLR = RCC_PLLR_DIV2;

  if (HAL_RCC_OscConfig(&oscillator) != HAL_OK) {
    Error_Handler();
  }

  clock.ClockType =
      RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
  clock.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  clock.AHBCLKDivider = RCC_SYSCLK_DIV1;
  clock.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&clock, FLASH_LATENCY_2) != HAL_OK) {
    Error_Handler();
  }
}

static void ldc_interrupt_init(void)
{
  GPIO_InitTypeDef gpio = {0};

  __HAL_RCC_GPIOC_CLK_ENABLE();

  gpio.Pin = GPIO_PIN_6;
  gpio.Mode = GPIO_MODE_INPUT;
  gpio.Pull = GPIO_NOPULL;
  gpio.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &gpio);
}

static void i2c_init(void)
{
  hi2c1.Instance = I2C1;
  /*
   * 10 kHz diagnostic timing for the 64 MHz I2C kernel clock. This is the
   * LDC1612 data-sheet minimum and gives maximum margin for ACK and edges.
   */
  hi2c1.Init.Timing = 0xF070C3C7U;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

  if (HAL_I2C_Init(&hi2c1) != HAL_OK ||
      HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK ||
      HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0U) != HAL_OK) {
    Error_Handler();
  }

  /* Required for the STM32G0B1 master-mode BERR workaround in ldc1612.c. */
  HAL_NVIC_SetPriority(I2C1_IRQn, 0U, 0U);
  HAL_NVIC_EnableIRQ(I2C1_IRQn);
}

void HAL_MspInit(void)
{
  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_RCC_PWR_CLK_ENABLE();

  /* The board is powered through USB-C, so disable the internal dead-battery
     pull-down resistors on the USB pins. */
  HAL_SYSCFG_StrobeDBattpinsConfig(
      SYSCFG_CFGR1_UCPD1_STROBE | SYSCFG_CFGR1_UCPD2_STROBE);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *i2c)
{
  GPIO_InitTypeDef gpio = {0};
  RCC_PeriphCLKInitTypeDef peripheral_clock = {0};

  if (i2c->Instance != I2C1) {
    return;
  }

  peripheral_clock.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
  peripheral_clock.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&peripheral_clock) != HAL_OK) {
    Error_Handler();
  }

  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_I2C1_CLK_ENABLE();

  gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  gpio.Mode = GPIO_MODE_AF_OD;
  gpio.Pull = GPIO_NOPULL;
  gpio.Speed = GPIO_SPEED_FREQ_LOW;
  gpio.Alternate = GPIO_AF6_I2C1;
  HAL_GPIO_Init(GPIOB, &gpio);
}

void SysTick_Handler(void)
{
  HAL_IncTick();
}

void Error_Handler(void)
{
  __disable_irq();
  while (1) {
  }
}
