/** @file tim.c
 * Module handling general purpose and advances TIMers
 */

//--includes--------------------------------------------------------------------

#include "tim.h"
#include "tim_regs.h"
#include "nvic.h"

#include "../srv/error.h"


//--local definitions-----------------------------------------------------------

static volatile struct TIM* regs[] = {
	(struct TIM*)TIM1_BASE_ADDRESS,
	(struct TIM*)TIM2_BASE_ADDRESS,
	(struct TIM*)TIM3_BASE_ADDRESS,
	(struct TIM*)TIM4_BASE_ADDRESS,
};

static enum TimIRQSource computeIRQSource(enum TimPeriph periph);


//--local variables-------------------------------------------------------------

static TimCallback callbacks[4];


//--public functions------------------------------------------------------------

void tim_configure_master(enum TimPeriph periph, enum TimConfig config_mask,
		enum TimMasterConfig master_config_mask, TimCallback callback)
{
	error_assert(periph <= TIM_PERIPH_4);

	//apply config mask directly while masking reserved areas. Masking is
	//important here since advanced timer may have additionnal config bits
	regs[periph]->cr1.word |= config_mask & 0x3FF;

	regs[periph]->cr2.word |= (config_mask >> 16) & 0x88;
	regs[periph]->cr2.word |= master_config_mask & 0x70;

	//if callback specified, configure IRQ
	if (callback != nullptr) {
		callbacks[periph] = callback;

		regs[periph]->dier.TIE = 1;
		regs[periph]->dier.UIE = 0;

		enum NvicIrq irq = 0;
		switch (periph) {
			case TIM_PERIPH_1:
				irq = NVIC_IRQ_TIM1_UP;
				break;
			case TIM_PERIPH_2:
				irq = NVIC_IRQ_TIM2;
				break;
			case TIM_PERIPH_3:
				irq = NVIC_IRQ_TIM3;
				break;
			case TIM_PERIPH_4:
				irq = NVIC_IRQ_TIM4;
				break;

		}
		nvic_enable(irq);
	}

	//trigger update to force application of the config
	regs[periph]->egr.UG = 1;
	while (regs[periph]->sr.UIF == 0) {}
	regs[periph]->sr.UIF = 0;

	if (callback != nullptr) {
		regs[periph]->dier.UIE = 1;
	}
}


void tim_start(enum TimPeriph periph)
{
	error_assert(periph <= TIM_PERIPH_4);
	regs[periph]->cr1.CEN = 1;
}

void tim_stop(enum TimPeriph periph)
{
	error_assert(periph <= TIM_PERIPH_4);
	regs[periph]->cr1.CEN = 0;
}

void tim_update(enum TimPeriph periph)
{
	error_assert(periph <= TIM_PERIPH_4);
	regs[periph]->egr.UG = 1;
}

void tim_set_auto_reload(enum TimPeriph periph, uint16_t auto_reload)
{
	error_assert(periph <= TIM_PERIPH_4);
	regs[periph]->arr.ARR = auto_reload;
}

void tim_set_prescaler(enum TimPeriph periph, uint16_t prescaler)
{
	error_assert(periph <= TIM_PERIPH_4);
	regs[periph]->psc.PSC = prescaler;
}

void tim_set_counter(enum TimPeriph periph, uint16_t counter)
{
	error_assert(periph <= TIM_PERIPH_4);
	regs[periph]->cnt.CNT = counter;
}

uint16_t tim_get_auto_reload(enum TimPeriph periph)
{
	error_assert(periph <= TIM_PERIPH_4);
	return regs[periph]->arr.ARR;
}

uint16_t tim_get_prescaler(enum TimPeriph periph)
{
	error_assert(periph <= TIM_PERIPH_4);
	return regs[periph]->psc.PSC;
}

uint16_t tim_get_counter(enum TimPeriph periph)
{
	error_assert(periph <= TIM_PERIPH_4);
	return regs[periph]->cnt.CNT;
}


//--local functions-------------------------------------------------------------

static enum TimIRQSource computeIRQSource(enum TimPeriph periph)
{
	enum TimIRQSource src =
		(TIM_IRQ_SOURCE_TRIGGER & regs[periph]->sr.TIF)
		| (TIM_IRQ_SOURCE_UPDATE & regs[periph]->sr.UIF);
	regs[periph]->sr.TIF = 0;
	regs[periph]->sr.UIF = 0;

	return src;
}

//--ISRs------------------------------------------------------------------------

void hdr_tim1_up(void)
{
	nvic_clear_pending(NVIC_IRQ_TIM1_UP);

	enum TimIRQSource src = computeIRQSource(TIM_PERIPH_1);
	callbacks[TIM_PERIPH_1](src);
}

void hdr_tim2(void)
{
	nvic_clear_pending(NVIC_IRQ_TIM2);

	enum TimIRQSource src = computeIRQSource(TIM_PERIPH_2);
	callbacks[TIM_PERIPH_2](src);
}

void hdr_tim3(void)
{
	nvic_clear_pending(NVIC_IRQ_TIM3);

	enum TimIRQSource src = computeIRQSource(TIM_PERIPH_3);
	callbacks[TIM_PERIPH_3](src);
}

void hdr_tim4(void)
{
	nvic_clear_pending(NVIC_IRQ_TIM4);

	enum TimIRQSource src = computeIRQSource(TIM_PERIPH_4);
	callbacks[TIM_PERIPH_4](src);
}

