#include "common.h"

#include <util/atomic.h>

void WDT_Enable(void)
{
	Info("Enabling watchdog timer...");

	// WDTCR: Watchdog Timer Control Register

	// 7   6   5   4      3    2     1     0
	// –   –   -   WDTOE  WDE  WDP2  WDP1  WDP0 

	// When the WDE is written to logic one, the
	// Watchdog Timer is enabled, and if the WDE is
	// written to logic zero, the Watchdog Timer
	// function is disabled.

	// WDP2, WDP1, WDP0: Watchdog Timer Prescaler
	// The WDP2, WDP1, and WDP0 bits determine the
	// Watchdog Timer prescaling when the Watchdog
	// Timer is enabled. The different prescaling
	// values and their corresponding Timeout
	// Periods are:

	// WDP2 WDP1 WDP0   Cycles  Timeout3V  Timeout5V
	//    0    0    0      16K    17.10ms    16.30ms
	//    0    0    1      32K    34.30ms    32.50ms
	//    0    1    0      64K    68.50ms    65.00ms
	//    0    1    1     128K     0.14s      0.13s
	//    1    0    0     256K     0.27s      0.26s
	//    1    0    1     512K     0.55s      0.52s
	//    1    1    0   1,024K     1.10s      1.00s
	//    1    1    1   2,048K     2.20s      2.10s

	// WDTOE: Watchdog Turn-off Enable This bit must
	// be set when the WDE bit is written to logic zero.
	// Otherwise, the Watchdog will not be disabled.
	// Once written to one, hardware will clear this
	// bit after four clock cycles.

	// 00001111: Watchdog enabled, 2sec timeout
	WDTCR = BIT(WDE) | BIT(WDP2) | BIT(WDP1) | BIT(WDP0);
}

void WDT_SetTimeoutFlag(byte flag)
{
	flag = CLAMP(flag, 7, 0);

	Info("Setting watchdog prescalar to %02X...", flag);

	// Clear timer prescalar flags
	WDTCR &= 0xF8; // 11111000

	WDTCR |= flag;
}

bool WDT_HasTriggered(void)
{
	bool isreset;

	// To make use of the Reset Flags to identify a reset
	// condition, the user should read and then reset the
	// MCUCSR as early as possible in the program. If the
	// register is cleared before another reset occurs,
	// the source of the reset can be found by examining
	// the Reset Flags.

	// MCUCSR: MCU Control and Status Register
	// The MCU Control and Status Register provides
	// information on which reset source caused an MCU
	// Reset.

	// 7    6      5    4     3     2     1     0
	// JTD  ISC2   –    JTRF  WDRF  BORF  EXTRF PORF 

	// Is power-on reset flag not set?
	isreset = ((MCUCSR & BIT(0)) == 0);

	// XXX: Reset flag detection should be a separate
	// module to handle the different types.

	MCUCSR = 0;

	return isreset;
}

void WDT_Disable(void)
{
	// WDE can only be cleared if the WDTOE bit has
	// logic level one. To disable an enabled watchdog
	// timer, the following procedure must be followed:

	// 1. In the same operation, write a logic one to
	// WDTOE and WDE. A logic one must be written to WDE
	// even though it is set to one before the disable
	// operation starts.

	// 2. Within the next four clock cycles, write a
	// logic 0 to WDE. This disables the watchdog.

	// No interrupts while we set WDTCR;
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
		WDTCR = BIT(WDTOE) | BIT(WDE);
		WDTCR = 0;
	}
}

void WDT_Reset(void)
{
	// The WDR – Watchdog Reset – instruction resets the
	// watchdog timer. The Watchdog Timer is also reset
	// when it is disabled and when a chip reset occurs.

	// If the reset period expires without another
	// watchdog reset, the chip resets and executes from
	// the Reset Vector.

	__asm__("WDR");
}
