blob: 10c6007090302652e8c77dd76eb3ff86134057d2 [file] [log] [blame]
/******************************************************************************
*
* alt_watchdog.c - API for the Altera SoC FPGA watchdog timers.
*
******************************************************************************/
/******************************************************************************
*
* Copyright 2013 Altera Corporation. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
******************************************************************************/
/******************************************************************************
*
* The Altera SoC FPGA has six watchdog timers, two are local to the MPU
* themselves, and the other four are accessable to either MPU.
*
******************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include "socal/hps.h"
#include "socal/socal.h"
#include "socal/alt_rstmgr.h"
#include "socal/alt_l4wd.h"
#include "socal/alt_tmr.h"
#include "hwlib.h"
#include "alt_mpu_registers.h"
#include "alt_watchdog.h"
#include "alt_clock_manager.h"
/* Useful constants and utilities */
bool cpu_wdog_in_gpt_mode(void)
{
return !(alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) & WDOG_WDT_MODE);
}
static inline bool cpu_wdog_in_wdt_mode(void)
{
return (alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) & WDOG_WDT_MODE);
}
/* This value must be written to the Counter Restart Register of the
* peripheral watchdog timers to restart them. */
#define WDOG_RESET_KEY 0x00000076
#define ALT_WDOG_RST_WIDTH 8 /* 8 or more MPU clock cycles */
inline static void alt_wdog_wait(void* reg, uint32_t cnt)
{
for (; cnt ; cnt--)
{
(void) alt_read_word(reg);
}
}
/****************************************************************************************/
/* Initialize the watchdog timer module before use */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_init(void)
{
// put watchdog timer modules into system manager reset if not already there
alt_wdog_uninit();
// release L4 watchdog timer modules from system reset (w/ four instruction-cycle delay)
alt_clrbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_L4WD0_SET_MSK |
ALT_RSTMGR_PERMODRST_L4WD1_SET_MSK);
// release *both* ARM watchdog timer modules from system reset (if in reset)
// does not put either one into watchdog timer mode
alt_clrbits_word(ALT_RSTMGR_MPUMODRST_ADDR, ALT_RSTMGR_MPUMODRST_WDS_SET_MSK);
return ALT_E_SUCCESS;
}
/****************************************************************************************/
/* Return the local ARM watchdog timer back to general-purpose timer mode */
/****************************************************************************************/
void alt_ARM_wdog_gpt_mode_set(void)
{
while (alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) & WDOG_WDT_MODE)
{
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_DISABLE_REG_OFFSET, WDOG_DISABLE_VAL0);
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_DISABLE_REG_OFFSET, WDOG_DISABLE_VAL1);
}
}
/****************************************************************************************/
/* Set the local ARM watchdog timer to watchdog timer mode */
/****************************************************************************************/
void alt_ARM_wdog_wdog_mode_set(void)
{
alt_setbits_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET, WDOG_WDT_MODE);
}
/****************************************************************************************/
/* Uninitialize the watchdog timer module & return to reset state */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_uninit(void)
{
// put L4 watchdog modules into system manager reset
alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR,
ALT_RSTMGR_PERMODRST_L4WD0_SET_MSK | ALT_RSTMGR_PERMODRST_L4WD1_SET_MSK);
// using the system manager bit to reset the ARM watchdog timer
// resets *both* ARM watchdog timers, which is often not advisable,
// so we reset the local ARM watchdog timer manually:
// first, stop the ARM watchdog timer & disable interrupt
alt_clrbits_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET, WDOG_TMR_ENABLE | WDOG_INT_EN);
// reset load and counter registers
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_LOAD_REG_OFFSET, 0);
// clear any pending reset and interrupt status
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_RSTSTAT_REG_OFFSET, WDOG_RST_STAT_BIT);
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_INTSTAT_REG_OFFSET, WDOG_INT_STAT_BIT);
// return ARM watchdog timer to (initial) general-purpose timer mode
alt_ARM_wdog_gpt_mode_set();
// now write zeros to the control register significant bitfields
// and then verify that all significant bitfields return zero
alt_clrbits_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET,
(WDOG_PS_MASK | WDOG_WDT_MODE | WDOG_INT_EN | WDOG_AUTO_RELOAD | WDOG_TMR_ENABLE));
if (alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET)
& (WDOG_PS_MASK | WDOG_WDT_MODE | WDOG_INT_EN | WDOG_AUTO_RELOAD | WDOG_TMR_ENABLE))
{
return ALT_E_ERROR;
}
return ALT_E_SUCCESS;
}
/****************************************************************************************/
/* Stops the specified watchdog timer. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_stop(ALT_WDOG_TIMER_t tmr_id)
{
ALT_STATUS_CODE ret = ALT_E_BAD_ARG; // return value
uint32_t config; // the current configuration
uint32_t loadreg; // current restart value
if (tmr_id == ALT_WDOG_CPU)
{
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET,
(alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) & ~WDOG_TMR_ENABLE));
ret = ALT_E_SUCCESS;
}
// these timers can only be reset by using a system manager reset
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
config = alt_read_word(ALT_L4WD0_WDT_CR_ADDR); // read current timer mode
loadreg = alt_read_word(ALT_L4WD0_WDT_TORR_ADDR); // read timer restart values
alt_write_word(ALT_RSTMGR_PERMODRST_ADDR,
alt_read_word(ALT_RSTMGR_PERMODRST_ADDR) | ALT_RSTMGR_PERMODRST_L4WD0_SET_MSK);
// assert reset & wait
alt_wdog_wait(ALT_RSTMGR_PERMODRST_ADDR, ALT_WDOG_RST_WIDTH);
alt_write_word(ALT_RSTMGR_PERMODRST_ADDR,
alt_read_word(ALT_RSTMGR_PERMODRST_ADDR) & ALT_RSTMGR_PERMODRST_L4WD0_CLR_MSK);
// release peripheral reset signal by clearing bit
alt_write_word(ALT_L4WD0_WDT_TORR_ADDR, loadreg); // restore timer restart value
alt_write_word(ALT_L4WD0_WDT_CR_ADDR, config & ALT_TMR_TMR1CTLREG_TMR1_EN_CLR_MSK);
// restore previous timer mode except timer isn't started
ret = ALT_E_SUCCESS;
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
config = alt_read_word(ALT_L4WD1_WDT_CR_ADDR); // read current timer mode
loadreg = alt_read_word(ALT_L4WD1_WDT_TORR_ADDR); // read timer restart values
alt_write_word(ALT_RSTMGR_PERMODRST_ADDR,
alt_read_word(ALT_RSTMGR_PERMODRST_ADDR) | ALT_RSTMGR_PERMODRST_L4WD1_SET_MSK);
// assert reset & wait
alt_write_word(ALT_RSTMGR_PERMODRST_ADDR,
alt_read_word(ALT_RSTMGR_PERMODRST_ADDR) & ALT_RSTMGR_PERMODRST_L4WD1_CLR_MSK);
// release peripheral reset signal by clearing bit
alt_write_word(ALT_L4WD1_WDT_TORR_ADDR, loadreg); // restore timer restart value
alt_write_word(ALT_L4WD1_WDT_CR_ADDR, config & ALT_TMR_TMR1CTLREG_TMR1_EN_CLR_MSK);
// restore previous timer mode except timer isn't started
ret = ALT_E_SUCCESS;
}
return ret;
}
/****************************************************************************************/
/* Start the specified watchdog timer. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_start(ALT_WDOG_TIMER_t tmr_id)
{
ALT_STATUS_CODE ret = ALT_E_BAD_ARG; // return value
uint32_t regdata; // data
if (tmr_id == ALT_WDOG_CPU)
{
regdata = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET);
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET, regdata | WDOG_TMR_ENABLE);
ret = ALT_E_SUCCESS;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
regdata = alt_read_word(ALT_L4WD0_WDT_CR_ADDR);
alt_write_word(ALT_L4WD0_WDT_CR_ADDR, regdata | ALT_L4WD_CR_WDT_EN_SET_MSK);
ret = ALT_E_SUCCESS;
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
regdata = alt_read_word(ALT_L4WD1_WDT_CR_ADDR);
alt_write_word(ALT_L4WD1_WDT_CR_ADDR, regdata | ALT_L4WD_CR_WDT_EN_SET_MSK);
ret = ALT_E_SUCCESS;
}
return ret;
}
/****************************************************************************************/
/* Returns whether the specified watchdog timer is currently running or not. */
/****************************************************************************************/
bool alt_wdog_tmr_is_enabled(ALT_WDOG_TIMER_t tmr_id)
{
bool ret = false; // return value
if (tmr_id == ALT_WDOG_CPU)
{
ret = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) & WDOG_TMR_ENABLE;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
ret = alt_read_word(ALT_L4WD0_WDT_CR_ADDR) & ALT_L4WD_CR_WDT_EN_SET_MSK;
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
ret = alt_read_word(ALT_L4WD1_WDT_CR_ADDR) & ALT_L4WD_CR_WDT_EN_SET_MSK;
}
return ret;
}
/****************************************************************************************/
/* Reloads the counter countdown value and restarts the watchdog timer. User can reset */
/* the timer at any time before timeout. Also known as kicking, petting, feeding, */
/* waking, or walking the watchdog. Inherently clears the interrupt as well. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_reset(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t regdata; // data read
if (tmr_id == ALT_WDOG_CPU)
{
regdata = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_LOAD_REG_OFFSET);
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_LOAD_REG_OFFSET, regdata);
// verify operation when we have hardware,
// the ARM documentation is somewhat vague here
if (cpu_wdog_in_wdt_mode())
{
alt_write_word((CPU_WDTGPT_TMR_BASE + WDOG_RSTSTAT_REG_OFFSET), WDOG_RST_STAT_BIT);
// depending on current mode, clear the reset bit or...
}
else
{
alt_write_word((CPU_WDTGPT_TMR_BASE + WDOG_INTSTAT_REG_OFFSET), WDOG_INT_STAT_BIT);
// ...clear the interrupt status bit by writing one to it
}
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
alt_write_word(ALT_L4WD0_WDT_CRR_ADDR, WDOG_RESET_KEY);
//restarts the counter, also clears the watchdog timer interrupt
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
alt_write_word(ALT_L4WD1_WDT_CRR_ADDR, WDOG_RESET_KEY);
//restarts the counter, also clears the watchdog timer interrupt
}
else {return ALT_E_BAD_ARG; }
return ALT_E_SUCCESS;
}
/****************************************************************************************/
/* Sets the countdown value of the specified timer. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_counter_set(ALT_WDOG_TIMER_t tmr_id, uint32_t val)
{
ALT_STATUS_CODE ret = ALT_E_BAD_ARG; // return value
uint32_t regdata; // returned data
if (tmr_id == ALT_WDOG_CPU)
{
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_LOAD_REG_OFFSET, val);
ret = ALT_E_SUCCESS;
// the ARM documentation is somewhat vague here, but it looks like it should be
// possible to rewrite this value while counter is running, and that it works in
// watchdog mode as well as timer mode. Verify operation when we have hardware.
}
else if (val <= ALT_WDOG_TIMEOUT2G)
{
if (tmr_id == ALT_WDOG0)
{
// set regular timeout value
regdata = alt_read_word(ALT_L4WD0_WDT_TORR_ADDR);
alt_write_word(ALT_L4WD0_WDT_TORR_ADDR, (regdata & ALT_L4WD_TORR_TOP_CLR_MSK) | val);
ret = ALT_E_SUCCESS;
}
else if (tmr_id == ALT_WDOG1)
{
// set regular timeout value
regdata = alt_read_word(ALT_L4WD1_WDT_TORR_ADDR);
alt_write_word(ALT_L4WD1_WDT_TORR_ADDR, (regdata & ALT_L4WD_TORR_TOP_CLR_MSK) | val);
ret = ALT_E_SUCCESS;
}
else if (tmr_id == ALT_WDOG0_INIT)
{
// set initial timeout value
regdata = alt_read_word(ALT_L4WD0_WDT_TORR_ADDR);
regdata = (regdata & ALT_L4WD_TORR_TOP_INIT_CLR_MSK) |
(val << ALT_L4WD_TORR_TOP_INIT_LSB);
alt_write_word(ALT_L4WD0_WDT_TORR_ADDR, regdata);
ret = ALT_E_SUCCESS;
}
else if (tmr_id == ALT_WDOG1_INIT)
{
// set initial timeout value
regdata = alt_read_word(ALT_L4WD1_WDT_TORR_ADDR);
regdata = (regdata & ALT_L4WD_TORR_TOP_INIT_CLR_MSK) |
(val << ALT_L4WD_TORR_TOP_INIT_LSB);
alt_write_word(ALT_L4WD1_WDT_TORR_ADDR, regdata);
ret = ALT_E_SUCCESS;
}
}
return ret;
}
/****************************************************************************************/
/* Returns the current counter value of the specified timer. */
/****************************************************************************************/
uint32_t alt_wdog_counter_get_current(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t ret = 0; // return value
if (tmr_id == ALT_WDOG_CPU)
{
ret = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CNTR_REG_OFFSET);
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
ret = alt_read_word(ALT_L4WD0_WDT_CCVR_ADDR);
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
ret = alt_read_word(ALT_L4WD1_WDT_CCVR_ADDR);
}
return ret;
}
/****************************************************************************************/
/* Returns the current counter value of the specified timer, as measured in */
/* milliseconds. For ALT_CPU_WATCHDOG, this includes the effects of the prescaler */
/* setting. */
/****************************************************************************************/
uint32_t alt_wdog_counter_get_curtime_millisecs(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t time = 0; // return value
uint64_t bigtime; // temp for math
alt_freq_t freq; // clock frequency
ALT_CLK_t clk; // clock ID
if (tmr_id == ALT_WDOG_CPU)
{
clk = ALT_CLK_MPU_PERIPH;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG1) ||
(tmr_id == ALT_WDOG0_INIT) || (tmr_id == ALT_WDOG1_INIT))
{
clk = ALT_CLK_OSC1;
}
else { return time; }
if ((alt_clk_freq_get(clk, &freq) == ALT_E_SUCCESS) && (freq != 0))
{ // get clock frequency & test
time = alt_wdog_counter_get_current(tmr_id); // get current counter value
if (time != 0)
{
bigtime = (uint64_t) time;
// the current time period is not counted, only whole periods are counted
if (tmr_id == ALT_WDOG_CPU)
{
bigtime *= (uint64_t) (alt_wdog_core_prescaler_get() + 1);
}
bigtime *= ALT_MILLISECS_IN_A_SEC;
bigtime /= freq; // cycles-per-second becomes milliseconds-per-cycle
time = (bigtime > (uint64_t) UINT32_MAX) ? 0 : (uint32_t) bigtime;
}
}
return time;
}
// see the return value range calculations below at alt_wdog_counter_get_inittime_millisecs().
/****************************************************************************************/
/* Returns the initial counter value of the specified timer as a 32-bit integer */
/* value. This is the value that will be reloaded when the timer is reset or restarted. */
/* For the timers where this value is set as an encoded powers-of-two between 15 and */
/* 31, the value is converted into the equivalent binary value before returning it. For */
/* ALT_CPU_WATCHDOG, the returned value does not include the effects of the prescaler */
/* setting */
/****************************************************************************************/
uint32_t alt_wdog_counter_get_init(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t ret = 0; // value to return
if (tmr_id == ALT_WDOG_CPU)
{
ret = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_LOAD_REG_OFFSET);
}
else if (tmr_id == ALT_WDOG0)
{
ret = ALT_L4WD_TORR_TOP_GET(alt_read_word(ALT_L4WD0_WDT_TORR_ADDR));
ret = (ret > ALT_L4WD_TORR_TOP_E_TMO2G) ? 0 : ALT_TWO_TO_POW16 << ret;
}
else if (tmr_id == ALT_WDOG1)
{
ret = ALT_L4WD_TORR_TOP_GET(alt_read_word(ALT_L4WD1_WDT_TORR_ADDR));
ret = (ret > ALT_L4WD_TORR_TOP_E_TMO2G) ? 0 : ALT_TWO_TO_POW16 << ret;
}
else if (tmr_id == ALT_WDOG0_INIT)
{
ret = ALT_L4WD_TORR_TOP_INIT_GET(alt_read_word(ALT_L4WD0_WDT_TORR_ADDR));
ret = (ret > ALT_L4WD_TORR_TOP_INIT_E_TMO2G) ? 0 : ALT_TWO_TO_POW16 << ret;
}
else if (tmr_id == ALT_WDOG1_INIT)
{
ret = ALT_L4WD_TORR_TOP_INIT_GET(alt_read_word(ALT_L4WD1_WDT_TORR_ADDR));
ret = (ret > ALT_L4WD_TORR_TOP_INIT_E_TMO2G) ? 0 : ALT_TWO_TO_POW16 << ret;
}
return ret;
}
/****************************************************************************************/
/* Returns the initial value of the specified timer in nanoseconds. This is the */
/* value that will be reloaded when the timer is reset or restarted. For */
/* ALT_CPU_WATCHDOG, this includes the effects of the prescaler setting. This call */
/* returns a more precise result than alt_wdog_counter_get_inittime_millisecs(), but */
/* as an unsigned 64-bit integer. */
/****************************************************************************************/
uint64_t alt_wdog_counter_get_inittime_nanosecs(ALT_WDOG_TIMER_t tmr_id)
{
uint64_t time = 0;
alt_freq_t freq;
ALT_CLK_t clk;
if (tmr_id == ALT_WDOG_CPU)
{
clk = ALT_CLK_MPU_PERIPH;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG1) ||
(tmr_id == ALT_WDOG0_INIT) || (tmr_id == ALT_WDOG1_INIT))
{
clk = ALT_CLK_OSC1;
}
else { return time; } // zero always indicates an error for an init time
if ((alt_clk_freq_get(clk, &freq) == ALT_E_SUCCESS) && (freq != 0))
{ // get clock frequency & test
time = (uint64_t) alt_wdog_counter_get_init(tmr_id); // get reset value
if (time != 0)
{
time += 1;
if (tmr_id == ALT_WDOG_CPU)
{
time *= (uint64_t) (alt_wdog_core_prescaler_get() + 1);
}
time *= ALT_NANOSECS_IN_A_SEC;
time /= freq; // cycles-per-second becomes nanoseconds per cycle
}
}
return time;
}
/* For reviewers:
* minimum clock divider for ALT_CPU_WATCHDOG is 1
* maximum clock divider for ALT_CPU_WATCHDOG is ((0xFFFF FFFF + 1) x (0x0000 0100) = 0x0000 0100 0000 0000)
* multiply that by the number of nanoseconds in a second (1,000,000,000)
* = 1,099,511,627,776,000,000,000 (0x9ACA 0000 0000 0000)
* so the countdown time with the slowest mpu_peripheral clock (2.5 MHz) =
* 400 nS to 439,804.6511104 seconds (0x0001 9000 0000 0000 nS)
* and with the fastest mpu_peripheral clock (200 MHz) =
* 5 nS to 5,497,558,138,880 nanoseconds ( 0x0000 0500 0000 0000 nS)
*
* minimum clock divider for peripheral watchdogs is 2**16 = (65,536 = 0x00010000)
* maximum clock divider for peripheral watchdogs is 2**31 = (2,147,483,648 = 0x8000 0000)
* multiply that by the number of nanoseconds in a second (1,000,000,000) =
* 4,096,000,000,000 (0x0000 03B9 ACA0 0000) to 2,147,483,648,000,000,000 (0x1DCD 6500 0000 0000)
* so the countdown time with the slowest l4_sp_clk (625 kHz) =
* 6,553,600 nS (0x0064 0000) to 3,435,973,836,800 nS (0x0000 0320 0000 0000 nS)
* and with the fastest l4_sp_clk (100 MHz) =
* 40,960 ns (0xA000) to 21,474,836,480 nS (0x0000 0005 0000 0000 nS)
*/
/****************************************************************************************/
/* Returns the initial value of the specified timer in milliseconds. This is the */
/* value that will be reloaded when the timer is reset or restarted. For */
/* ALT_CPU_WATCHDOG, this includes the effects of the prescaler setting. This call */
/* returns a 32-bit unsigned integer, though is less precise than */
/* alt_wdog_counter_get_inittime_nanosecs(). */
/****************************************************************************************/
uint32_t alt_wdog_counter_get_inittime_millisecs(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t time = 0;
alt_freq_t freq;
ALT_CLK_t clk;
uint64_t bigtime;
if (tmr_id == ALT_WDOG_CPU)
{
clk = ALT_CLK_MPU_PERIPH;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG1) ||
(tmr_id == ALT_WDOG0_INIT) || (tmr_id == ALT_WDOG1_INIT))
{
clk = ALT_CLK_OSC1;
}
else { return time; } // must be an invalid tmr_id
if ((alt_clk_freq_get(clk, &freq) == ALT_E_SUCCESS) && (freq != 0))
{ // get clock frequency & test
time = alt_wdog_counter_get_init(tmr_id); // get reset value
if (time != 0)
{
bigtime = ((uint64_t) time) + 1;
if (tmr_id == ALT_WDOG_CPU) // the only watchdog with a prescaler
{
bigtime *= (uint64_t) (alt_wdog_core_prescaler_get() + 1);
}
bigtime *= ALT_MILLISECS_IN_A_SEC; // scale value
bigtime /= freq; // cycles-per-second becomes milliseconds per cycle
time = (bigtime > (uint64_t) UINT32_MAX) ? 0 : (uint32_t) bigtime;
}
}
return time;
}
/* For reviewers:
* minimum clock divider for ALT_CPU_WATCHDOG is 1
* maximum clock divider for ALT_CPU_WATCHDOG is ((0xFFFF FFFF + 1) x (0x0000 0100) = 0x0000 0100 0000 0000)
* multiply that by the number of milliseconds in a second (1,000)
* = 1,000 (0x3e8) to 1,099,511,627,776,000 (0x0003 E800 0000 0000)
* so the countdown time with the slowest mpu_peripheral clock (2.5 MHz) =
* 0 mS to 439,804.6511104 seconds (0x1A36 E2EB mS)
* and with the fastest mpu_peripheral clock (200 MHz) =
* 0 mS to 5,497.55813888 seconds ( 0x0053 E2D6 mS)
*
* minimum clock divider for peripheral watchdogs is 2**16 = (65,536 = 0x00010000)
* maximum clock divider for peripheral watchdogs is 2**31 = (2,147,483,648 = 0x8000 0000)
* multiply that by the number of milliseconds in a second (1,000) =
* 65,536,000 (0x3E8 0000) to 2,147,483,648,000 (0x01F4 0000 0000)
* so the countdown time with the slowest l4_sp_clk (625 kHz) =
* 104 mS (0x0068) to 3,435,973 mS (0x0034 6DC5 mS)
* and with the fastest l4_sp_clk (100 MHz) = 0 mS to 21,474 mS (0x0000 53E2 mS)
*/
/****************************************************************************************/
/* Sets the value of the CPU watchdog timer ALT_CPU_WATCHDOG prescaler. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_core_prescaler_set(uint32_t val)
{
ALT_STATUS_CODE ret = ALT_E_BAD_ARG; // return value
uint32_t regdata;
if (val <= WDOG_PS_MAX)
{
if (alt_wdog_tmr_is_enabled(ALT_WDOG_CPU))
{
ret = ALT_E_ERROR;
}
else
{
regdata = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET);
alt_write_word((CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET),
(regdata & ~WDOG_PS_MASK) | (val << WDOG_PS_SHIFT));
ret = ALT_E_SUCCESS;
}
}
return ret;
}
/****************************************************************************************/
/* Returns the value of the prescaler of the CPU core watchdog timer. */
/****************************************************************************************/
uint32_t alt_wdog_core_prescaler_get(void)
{
return (alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) &
WDOG_PS_MASK) >> WDOG_PS_SHIFT;
}
/****************************************************************************************/
/* Returns the maximum possible counter value of the specified timer as a 32-bit value. */
/* For the timers where this value is encoded (as powers-of-two between 15 and 31), the */
/* encoded value is converted into the equivalent binary value before returning it. */
/* This does not include the effects of the prescaler available for ALT_CPU_WATCHDOG. */
/****************************************************************************************/
uint32_t alt_wdog_counter_get_max(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t ret = 0; // return value
if (tmr_id == ALT_WDOG_CPU)
{
ret = WDOG_TMR_MAX;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG1)
|| (tmr_id == ALT_WDOG0_INIT) || (tmr_id == ALT_WDOG1_INIT))
{
ret = ((uint32_t) ALT_TWO_TO_POW16) << ALT_WDOG_TIMEOUT2G;
}
return ret;
}
/****************************************************************************************/
/* Returns the maximum possible delay time of the specified timer specified in */
/* nanoseconds. For ALT_CPU_WATCHDOG, this includes the prescaler setting. This call */
/* returns a more precise reading of the counter than */
/* alt_wdog_counter_get_max_millisecs(), though in an unsigned 64-bit integer. */
/****************************************************************************************/
uint64_t alt_wdog_counter_get_max_nanosecs(ALT_WDOG_TIMER_t tmr_id)
{
uint64_t time = 0;
alt_freq_t freq;
ALT_CLK_t clk;
if (tmr_id == ALT_WDOG_CPU)
{
clk = ALT_CLK_MPU_PERIPH;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG1) ||
(tmr_id == ALT_WDOG0_INIT) || (tmr_id == ALT_WDOG1_INIT))
{
clk = ALT_CLK_OSC1;
}
else { return time; }
if ((alt_clk_freq_get(clk, &freq) == ALT_E_SUCCESS) && (freq != 0))
{ // get clock frequency & test
time = (uint64_t) alt_wdog_counter_get_max(tmr_id); // get maximum reset value
if (time != 0)
{
time += 1;
if (tmr_id == ALT_WDOG_CPU)
{
time *= (WDOG_PS_MAX + 1); // maximum prescaler
}
time *= ALT_NANOSECS_IN_A_SEC;
time /= freq; //cycles-per-second becomes nanoseconds-per-cycle
}
}
return time;
}
/****************************************************************************************/
/* Returns the maximum possible delay time of the specified timer specified in */
/* milliseconds. For ALT_CPU_WATCHDOG, this includes the prescaler setting. This call */
/* returns a 32-bit unsigned integer, though is less precise than */
/* alt_wdog_counter_get_max_nanosecs(). */
/****************************************************************************************/
uint32_t alt_wdog_counter_get_max_millisecs(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t time = 0;
alt_freq_t freq;
ALT_CLK_t clk;
uint64_t bigtime;
if (tmr_id == ALT_WDOG_CPU)
{
clk = ALT_CLK_MPU_PERIPH;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG1) ||
(tmr_id == ALT_WDOG0_INIT) || (tmr_id == ALT_WDOG1_INIT))
{
clk = ALT_CLK_OSC1;
}
else { return time; }
if ((alt_clk_freq_get(clk, &freq) == ALT_E_SUCCESS) && (freq != 0))
{ // get clock frequency & test
time = alt_wdog_counter_get_max(tmr_id); // get reset value
if (time != 0)
{
bigtime = ((uint64_t) time) + 1;
if (tmr_id == ALT_WDOG_CPU)
{
bigtime *= (WDOG_PS_MAX + 1); // maximum prescaler
}
bigtime *= ALT_MILLISECS_IN_A_SEC;
bigtime /= freq; //cycles-per-second becomes milliseconds-per-cycle
time = (bigtime > (uint64_t) UINT32_MAX) ? 0 : (uint32_t) bigtime;
}
}
return time;
}
/****************************************************************************************/
/* Disables the interrupt of the specified watchdog timer module. If the watchdog timer */
/* is one of the watchdog timers that can be used in general-purpose mode, and if the */
/* timer is in general-purpose timer mode, disable the interrupt. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_int_disable(ALT_WDOG_TIMER_t tmr_id)
{
ALT_STATUS_CODE ret = ALT_E_BAD_ARG; // return value
if (tmr_id == ALT_WDOG_CPU)
{
if (cpu_wdog_in_wdt_mode())
{
ret = ALT_E_ERROR;
}
else
{
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET,
(alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) & ~WDOG_INT_EN));
ret = ALT_E_SUCCESS;
}
}
// returns an error for the other four watchdog timers
// since their interrupts cannot be disabled
// (this could change in v13.1)
return ret;
}
/****************************************************************************************/
/* Sets/enables the interrupt of the specified watchdog timer module. If the watchdog */
/* timer is one of the watchdog timers that can be used in general-purpose mode, and */
/* if the timer is in general-purpose timer mode, enable the interrupt. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_int_enable(ALT_WDOG_TIMER_t tmr_id)
{
ALT_STATUS_CODE ret = ALT_E_BAD_ARG; // return value
if (tmr_id == ALT_WDOG_CPU)
{
if (cpu_wdog_in_wdt_mode())
{
ret = ALT_E_ERROR;
}
else
{
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET,
(alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) | WDOG_INT_EN));
ret = ALT_E_SUCCESS;
}
}
return ret;
// other watchdog timers always have interrupt enabled if they are running
}
/****************************************************************************************/
/* Returns the status of the interrupt of the specified watchdog timer module but does */
/* not clear it. Return TRUE if the interrupt of the specified general purpose timer */
/* module is pending and FALSE otherwise. */
/****************************************************************************************/
bool alt_wdog_int_is_pending(ALT_WDOG_TIMER_t tmr_id)
{
bool ret = false; //return value
if ((tmr_id == ALT_WDOG_CPU) && cpu_wdog_in_gpt_mode())
{
ret = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_INTSTAT_REG_OFFSET) & WDOG_INT_STAT_BIT;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
ret = alt_read_word(ALT_L4WD0_WDT_STAT_ADDR) & ALT_L4WD_STAT_WDT_STAT_SET_MSK;
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
ret = alt_read_word(ALT_L4WD1_WDT_STAT_ADDR) & ALT_L4WD_STAT_WDT_STAT_SET_MSK;
}
return ret;
}
/****************************************************************************************/
/* Returns the state of the interrupt of the specified watchdog timer module. If the */
/* watchdog timer is one of the watchdog timers that can be used in general-purpose */
/* mode, and if the timer is in general-purpose timer mode, returns TRUE if the */
/* interrupt of the specified general purpose timer module is enabled and FALSE if */
/* disabled. If the timer is not in general-purpose timer mode, returns TRUE, as */
/* watchdog interrupts are always enabled. */
/****************************************************************************************/
bool alt_wdog_int_is_enabled(ALT_WDOG_TIMER_t tmr_id)
{
bool ret = false; //return value
if (tmr_id == ALT_WDOG_CPU)
{
ret = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET) &
(WDOG_INT_EN | WDOG_WDT_MODE);
// if in watchdog mode OR if in general purpose timer mode
// AND the interrupt is enabled
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
ret = alt_read_word(ALT_L4WD0_WDT_CR_ADDR) & ALT_L4WD_CR_WDT_EN_SET_MSK;
// if these timers are running, their interrupt is enabled
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
ret = alt_read_word(ALT_L4WD1_WDT_CR_ADDR) & ALT_L4WD_CR_WDT_EN_SET_MSK;
// if these timers are running, their interrupt is enabled
}
return ret;
}
/****************************************************************************************/
/* Clears the pending status of the interrupt of the specified watchdog timer module. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_int_clear(ALT_WDOG_TIMER_t tmr_id)
{
if (tmr_id == ALT_WDOG_CPU)
{
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_INTSTAT_REG_OFFSET, WDOG_INT_STAT_BIT);
// clear int by writing to status bit
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
(void) alt_read_word(ALT_L4WD0_WDT_EOI_ADDR);
// clear int by reading from end-of-interrupt register
// adding the void cast tells armcc not to throw a error for this usage
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
(void) alt_read_word(ALT_L4WD1_WDT_EOI_ADDR);
// clear int by reading from end-of-interrupt register
}
else {return ALT_E_ERROR; }
return ALT_E_SUCCESS;
}
/****************************************************************************************/
/* Returns the status of the interrupt of the specified watchdog timer module and also */
/* clears it. Return TRUE if the interrupt of the specified general purpose timer */
/* module is pending and FALSE otherwise. */
/****************************************************************************************/
bool alt_wdog_int_if_pending_clear(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t ret = false; // value to return
if (tmr_id == ALT_WDOG_CPU)
{
ret = (alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_INTSTAT_REG_OFFSET) & WDOG_INT_STAT_BIT);
if (ret)
{
alt_write_word(CPU_WDTGPT_TMR_BASE + WDOG_INTSTAT_REG_OFFSET, WDOG_INT_STAT_BIT);
// clear int by writing to status bit
}
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
ret = alt_read_word(ALT_L4WD0_WDT_STAT_ADDR) & ALT_L4WD_STAT_WDT_STAT_SET_MSK;
if (ret)
{
(void) alt_read_word(ALT_L4WD0_WDT_EOI_ADDR);
// clear int by reading from end-of-interrupt register
// adding the void cast tells armcc not to throw a error for this usage
}
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
ret = alt_read_word(ALT_L4WD1_WDT_STAT_ADDR) & ALT_L4WD_STAT_WDT_STAT_SET_MSK;
if (ret)
{
(void) alt_read_word(ALT_L4WD1_WDT_EOI_ADDR);
// clear int by reading from end-of-interrupt register
}
}
return ret;
}
/****************************************************************************************/
/* Sets the timeout response mode of the specified watchdog timer. For ALT_WATCHDOG0, */
/* ALT_WATCHDOG1, \b ALT_WATCHDOG0_INITIAL or \b ALT_WATCHDOG1_INITIAL, the options */
/* are to generate a system reset or to generate an interrupt and then generate a */
/* system reset if the interrupt is not cleared by the next time the watchdog timer */
/* counter rolls over. For ALT_CPU_WATCHDOG, the options are to trigger an interrupt */
/* request (with the result set in the interrupt manager) or a reset request (with the */
/* result set in the reset manager) plus two more options available when it is used */
/* as a general-purpose timer. */
/****************************************************************************************/
ALT_STATUS_CODE alt_wdog_response_mode_set(ALT_WDOG_TIMER_t tmr_id, ALT_WDOG_RESET_TYPE_t type)
{
ALT_STATUS_CODE ret = ALT_E_BAD_ARG; // return value
uint32_t regdata; // register data
if (tmr_id == ALT_WDOG_CPU)
{
regdata = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET);
if (type == ALT_WDOG_TIMER_MODE_ONESHOT)
{
alt_write_word((CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET), regdata & ~WDOG_AUTO_RELOAD);
ret = ALT_E_SUCCESS;
}
else if (type == ALT_WDOG_TIMER_MODE_FREERUN)
{
alt_write_word((CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET), regdata | WDOG_AUTO_RELOAD);
ret = ALT_E_SUCCESS;
}
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
regdata = alt_read_word(ALT_L4WD0_WDT_CR_ADDR);
if (type == ALT_WDOG_WARM_RESET)
{
alt_write_word(ALT_L4WD0_WDT_CR_ADDR, regdata & ALT_L4WD_CR_RMOD_CLR_MSK);
ret = ALT_E_SUCCESS;
}
else if (type == ALT_WDOG_INT_THEN_RESET)
{
alt_write_word(ALT_L4WD0_WDT_CR_ADDR, regdata | ALT_L4WD_CR_RMOD_SET_MSK);
ret = ALT_E_SUCCESS;
}
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
regdata = alt_read_word(ALT_L4WD1_WDT_CR_ADDR);
if (type == ALT_WDOG_WARM_RESET)
{
alt_write_word(ALT_L4WD1_WDT_CR_ADDR, regdata & ALT_L4WD_CR_RMOD_CLR_MSK);
ret = ALT_E_SUCCESS;
}
else if (type == ALT_WDOG_INT_THEN_RESET)
{
alt_write_word(ALT_L4WD1_WDT_CR_ADDR, regdata | ALT_L4WD_CR_RMOD_SET_MSK);
ret = ALT_E_SUCCESS;
}
}
return ret; // rejects a bad tmr_id argument/type argument combination
}
/****************************************************************************************/
/* Returns the response mode of the specified timer. */
/****************************************************************************************/
int32_t alt_wdog_response_mode_get(ALT_WDOG_TIMER_t tmr_id)
{
int32_t ret = ALT_E_BAD_ARG; // return value
uint32_t regdata; // read value
if (tmr_id == ALT_WDOG_CPU)
{
regdata = alt_read_word(CPU_WDTGPT_TMR_BASE + WDOG_CTRL_REG_OFFSET);
ret = (regdata & WDOG_AUTO_RELOAD) ? ALT_WDOG_TIMER_MODE_FREERUN : ALT_WDOG_TIMER_MODE_ONESHOT;
}
else if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
regdata = alt_read_word(ALT_L4WD0_WDT_CR_ADDR);
ret = (regdata & ALT_L4WD_CR_RMOD_SET_MSK) ? ALT_WDOG_INT_THEN_RESET : ALT_WDOG_WARM_RESET;
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
regdata = alt_read_word(ALT_L4WD1_WDT_CR_ADDR);
ret = (regdata & ALT_L4WD_CR_RMOD_SET_MSK) ? ALT_WDOG_INT_THEN_RESET : ALT_WDOG_WARM_RESET;
}
return ret;
}
/****************************************************************************************/
/* Returns the component code of the watchdog timer module. Only valid for */
/* ALT_WATCHDOG0, ALT_WATCHDOG1, ALT_WATCHDOG0_INITIAL or ALT_WATCHDOG1_INITIAL. */
/****************************************************************************************/
uint32_t alt_wdog_compcode_get(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t component = 0; // component code of the module
if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
component = alt_read_word(ALT_L4WD0_WDT_COMP_TYPE_ADDR);
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
component = alt_read_word(ALT_L4WD1_WDT_COMP_TYPE_ADDR);
}
return component;
}
/****************************************************************************************/
/* Returns the version code of the watchdog timer module. Only valid for ALT_WATCHDOG0, */
/* ALT_WATCHDOG1, ALT_WATCHDOG0_INITIAL or ALT_WATCHDOG1_INITIAL. */
/****************************************************************************************/
uint32_t alt_wdog_ver_get(ALT_WDOG_TIMER_t tmr_id)
{
uint32_t ver = 0; // revision code of the module
if ((tmr_id == ALT_WDOG0) || (tmr_id == ALT_WDOG0_INIT))
{
ver = alt_read_word(ALT_L4WD0_WDT_COMP_VER_ADDR);
}
else if ((tmr_id == ALT_WDOG1) || (tmr_id == ALT_WDOG1_INIT))
{
ver = alt_read_word(ALT_L4WD1_WDT_COMP_VER_ADDR);
}
return ver;
}
/****************************************************************************************/