/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */

/*
 * ----------------------------------------------------------------------------------
 * The code in this file is copied (with a few modifications) from:
 *     core_esp8266_timer.cpp - timer1 library for esp8266
 * ----------------------------------------------------------------------------------
 * Copyright (C) 2015 Hristo Gochkov. All rights reserved.
 *
 * This file is part of the esp8266 core for Arduino environment.
 *
 * This library 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 2.1 of the License, or (at your option) any later
 * version.
 *
 * This library 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 library; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 * ----------------------------------------------------------------------------------
 */


#include "CoreLib.h"


namespace eCxx {

namespace NodeMCU {


//
// Timer0
//
static volatile timercallback timer0UserCallback = nullptr;


static void ICACHE_RAM_ATTR timer0_isr(void*)
{
    if(!timer0UserCallback) return;

    // Stop other interrupts before calling the user callback
    AtomicGuard g;
    (*timer0UserCallback)();
}


extern void ICACHE_RAM_ATTR timer0InitISR()
{ ETS_CCOMPARE0_INTR_ATTACH(timer0_isr, nullptr); }


extern void ICACHE_RAM_ATTR timer0AttachInterrupt(timercallback userFunc)
{
    timer0UserCallback = userFunc;
    ETS_CCOMPARE0_ENABLE();
}


extern void ICACHE_RAM_ATTR timer0DetachInterrupt()
{
    timer0UserCallback = nullptr;
    ETS_CCOMPARE0_DISABLE();
}


//
// Timer1
//
static volatile timercallback timer1UserCallback = nullptr;


static void ICACHE_RAM_ATTR timer1_isr(void*)
{
    if( ( T1C & ( _BV(TCAR) | _BV(TCIT) ) ) == 0 ) TEIE &= ~TEIE1; // Edge interrupt disable
    T1I = 0;

    if(!timer1UserCallback) return;

    // Stop other interrupts before calling the user callback
    AtomicGuard g;
    (*timer1UserCallback)();
}


extern void ICACHE_RAM_ATTR timer1InitISR()
{ ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr, nullptr); }


extern void ICACHE_RAM_ATTR timer1AttachInterrupt(timercallback userFunc)
{
    timer1UserCallback = userFunc;
    ETS_FRC1_INTR_ENABLE();
}


extern void ICACHE_RAM_ATTR timer1DetachInterrupt()
{
    timer1UserCallback = nullptr;

    TEIE &= ~TEIE1; // Edge interrupt disable

    ETS_FRC1_INTR_DISABLE();
}


extern void ICACHE_RAM_ATTR timer1Enable(uint8_t divider, uint8_t intType, uint8_t reload)
{
    T1C = _BV(TCTE) | ( (divider & 3) << TCPD ) | ( (intType & 1) << TCIT ) | ( (reload & 1) << TCAR );
    T1I = 0;
}


extern void ICACHE_RAM_ATTR timer1Disable()
{
    T1C = 0;
    T1I = 0;
}


extern void ICACHE_RAM_ATTR timer1Write(uint32_t ticks)
{
    T1L = (ticks & 0x7FFFFF);
    if( ( T1C & _BV(TCIT) ) == 0 ) TEIE |= TEIE1; // Edge interrupt enable
}


} // namespace NodeMCU

} // namespace eCxx
