// Copyright 2020 Embedded Artistry LLC
// SPDX-License-Identifier: MIT

#include "STM32ClockControl.hpp"
#include <STM32Timer.hpp>
#include <STM32UART.hpp>
#include <array>
#include <processor_includes.hpp>
#include <stm32h7xx_ll_rcc.h>
#include <volatile.hpp>

namespace
{
#if 0 // TODO - GPIO not handled yet
constexpr std::array<unsigned, 9> gpio_enable_bits = {
	RCC_AHB2ENR_GPIOAEN, RCC_AHB2ENR_GPIOBEN, RCC_AHB2ENR_GPIOCEN,
	RCC_AHB2ENR_GPIODEN, RCC_AHB2ENR_GPIOEEN, RCC_AHB2ENR_GPIOFEN,
	RCC_AHB2ENR_GPIOGEN, RCC_AHB2ENR_GPIOHEN, RCC_AHB2ENR_GPIOIEN};
#endif

constexpr std::array<volatile uint32_t* const, STM32Timer::Device::COUNT> timer_enable_reg = {
	&RCC->AHB2ENR,	&RCC->APB1LENR, &RCC->APB1LENR, &RCC->APB1LENR,
	&RCC->APB1LENR, &RCC->APB1LENR, &RCC->APB1LENR, &RCC->AHB2ENR};

constexpr std::array<unsigned, STM32Timer::Device::COUNT> timer_enable_bits = {
	RCC_APB2ENR_TIM1EN,	 RCC_APB1LENR_TIM2EN, RCC_APB1LENR_TIM3EN, RCC_APB1LENR_TIM4EN,
	RCC_APB1LENR_TIM5EN, RCC_APB1LENR_TIM6EN, RCC_APB1LENR_TIM7EN, RCC_APB2ENR_TIM8EN};

constexpr std::array<volatile uint32_t* const, 4> i2c_enable_reg = {&RCC->APB1LENR, &RCC->APB1LENR,
																	&RCC->APB1LENR, &RCC->APB4ENR};
constexpr std::array<unsigned, 4> i2c_enable_bits = {RCC_APB1LENR_I2C1EN, RCC_APB1LENR_I2C2EN,
													 RCC_APB1LENR_I2C3EN, RCC_APB4ENR_I2C4EN};

constexpr std::array<volatile uint32_t* const, STM32UART::devices::numUARTs> uart_enable_reg = {
	&RCC->APB2ENR, &RCC->APB1LENR, &RCC->APB1LENR, &RCC->APB1LENR, &RCC->APB1LENR,
	&RCC->APB2ENR, &RCC->APB1LENR, &RCC->APB1LENR, &RCC->APB2ENR,  &RCC->APB2ENR};

constexpr std::array<unsigned, STM32UART::devices::numUARTs> uart_enable_bits = {
	RCC_APB2ENR_USART1EN, RCC_APB1LENR_USART2EN, RCC_APB1LENR_USART3EN, RCC_APB1LENR_UART4EN,
	RCC_APB1LENR_UART5EN, RCC_APB2ENR_USART6EN,	 RCC_APB1LENR_UART7EN,	RCC_APB1LENR_UART8EN,
	RCC_APB2ENR_UART9EN,  RCC_APB2ENR_USART10EN};

constexpr std::array<unsigned, 2> dma_enable_bits = {RCC_AHB1ENR_DMA1EN, RCC_AHB1ENR_DMA2EN};

}; // namespace

#if 0 // TODO - GPIO not handled yet
void STM32ClockControl::gpioEnable(embvm::gpio::port port) noexcept
{
	uint32_t val = embutil::volatile_load(&RCC->AHB2ENR);
	val |= gpio_enable_bits[port];
	embutil::volatile_store(&RCC->AHB2ENR, val);
}

void STM32ClockControl::gpioDisable(embvm::gpio::port port) noexcept
{
	uint32_t val = embutil::volatile_load(&RCC->AHB2ENR);
	val &= ~(gpio_enable_bits[port]);
	embutil::volatile_store(&RCC->AHB2ENR, val);
}
#endif

void STM32ClockControl::i2cEnable(uint8_t device) noexcept
{
	volatile uint32_t* const reg = i2c_enable_reg[device];
	assert(reg);
	uint32_t val = embutil::volatile_load(reg);
	val |= i2c_enable_bits[device];
	embutil::volatile_store(reg, val);
}

void STM32ClockControl::i2cDisable(uint8_t device) noexcept
{
	volatile uint32_t* const reg = i2c_enable_reg[device];
	assert(reg);
	uint32_t val = embutil::volatile_load(reg);
	val &= ~(i2c_enable_bits[device]);
	embutil::volatile_store(reg, val);
}

void STM32ClockControl::timerEnable(uint8_t timer) noexcept
{
	volatile uint32_t* const reg = timer_enable_reg[timer];
	assert(reg); // Check for a valid timer channel
	uint32_t val = embutil::volatile_load(reg);
	val |= timer_enable_bits[timer];
	embutil::volatile_store(reg, val);
}

void STM32ClockControl::timerDisable(uint8_t timer) noexcept
{
	volatile uint32_t* const reg = timer_enable_reg[timer];
	assert(reg); // Check for a valid timer channel
	uint32_t val = embutil::volatile_load(reg);
	val &= ~(timer_enable_bits[timer]);
	embutil::volatile_store(reg, val);
}

void STM32ClockControl::uartEnable(uint8_t device) noexcept
{
	volatile uint32_t* const reg = uart_enable_reg[device];
	assert(reg);
	uint32_t val = embutil::volatile_load(reg);
	val |= uart_enable_bits[device];
	embutil::volatile_store(reg, val);
}

void STM32ClockControl::uartDisable(uint8_t device) noexcept
{
	volatile uint32_t* const reg = uart_enable_reg[device];
	assert(reg);
	uint32_t val = embutil::volatile_load(reg);
	val &= ~(uart_enable_bits[device]);
	embutil::volatile_store(reg, val);
}
void STM32ClockControl::dmaEnable(uint8_t device) noexcept
{
	uint32_t val = embutil::volatile_load(&(RCC->AHB1ENR));
	val |= dma_enable_bits[device];
	embutil::volatile_store(&(RCC->AHB1ENR), val);
}

void STM32ClockControl::dmaDisable(uint8_t device) noexcept
{
	uint32_t val = embutil::volatile_load(&(RCC->AHB1ENR));
	val &= ~(dma_enable_bits[device]);
	embutil::volatile_store(&(RCC->AHB1ENR), val);
}
