/*
 * Copyright (C) 2023 Daniele Lacamera <root@danielinux.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*  
 *      This is free software: you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License version 2, as 
 *      published by the Free Software Foundation.
 *
 *      frosted is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with frosted.  If not, see <http://www.gnu.org/licenses/>.
 *
 *      Author: Daniele Lacamera
 *      
 *
 */  
#include <stdint.h>
#include "system.h"
#include <unicore-mx/cm3/nvic.h>
#include <unicore-mx/stm32/rcc.h>

extern int AudioPulse;



#define TIM2_BASE (0x40000000)
#define TIM2_CR1  (*(volatile uint32_t *)(TIM2_BASE + 0x00))
#define TIM2_DIER (*(volatile uint32_t *)(TIM2_BASE + 0x0c))
#define TIM2_CNT  (*(volatile uint32_t *)(TIM2_BASE + 0x24))
#define TIM2_PSC  (*(volatile uint32_t *)(TIM2_BASE + 0x28))
#define TIM2_ARR  (*(volatile uint32_t *)(TIM2_BASE + 0x2c))

#define TIM_DIER_UIE (1 << 0)
#define TIM_CR1_CLOCK_ENABLE (1 << 0)
#define TIM_CR1_UPD_RS       (1 << 2)
#define TIM_CR1_EV_DISABLE   (1 << 1)
#define TIM2_SR   (*(volatile uint32_t *)(TIM2_BASE + 0x10))
#define TIM_SR_UIF   (1 << 0)


int timer_start(uint32_t clock, uint32_t prescaler, uint32_t interval_us)
{
    uint32_t val = 0;
    uint32_t psc = 1;
    uint32_t err = 0;
    uint32_t reg = 0;
    clock = ((clock * prescaler) / 2000000) * interval_us;

    while (psc < 65535) {
        val = clock / psc;
        err = clock % psc;
        if ((val < 65535) && (err == 0)) {
            val--;
            break;
        }
        val = 0;
        psc++;
    }
    if (val == 0)
        return -1;

    nvic_irq_enable(NVIC_TIM2_IRQN);
    nvic_set_priority(NVIC_TIM2_IRQN, 3);
    APB1_CLOCK_RST |= TIM2_APB1_CLOCK_ER_VAL;
    DMB();
    APB1_CLOCK_RST &= ~TIM2_APB1_CLOCK_ER_VAL;
    APB1_CLOCK_ER |= TIM2_APB1_CLOCK_ER_VAL;

    TIM2_CR1    = 0;
    DMB();
    TIM2_PSC    = psc;
    TIM2_ARR    = val;
    TIM2_CNT    = val - 1;
    reg = TIM2_CR1;
    reg &= ~TIM_CR1_EV_DISABLE;
    reg |= TIM_CR1_CLOCK_ENABLE;
    TIM2_CR1 = reg;
    TIM2_DIER   |= TIM_DIER_UIE;
    DMB();
    return 0;
}

void timer_stop(void)
{
    APB1_CLOCK_RST |= TIM2_APB1_CLOCK_ER_VAL;
    DMB();
    APB1_CLOCK_RST &= ~TIM2_APB1_CLOCK_ER_VAL;
    TIM2_CR1    |= TIM_CR1_EV_DISABLE;
    APB1_CLOCK_ER &= ~TIM2_APB1_CLOCK_ER_VAL;
}

void isr_tim2(void) {
    TIM2_SR &= ~TIM_SR_UIF;
//    AudioPulse++;
}
