diff options
Diffstat (limited to 'src/drivers/Watchdog.cpp')
| -rw-r--r-- | src/drivers/Watchdog.cpp | 168 |
1 files changed, 121 insertions, 47 deletions
diff --git a/src/drivers/Watchdog.cpp b/src/drivers/Watchdog.cpp index d0907a65..6c2c963b 100644 --- a/src/drivers/Watchdog.cpp +++ b/src/drivers/Watchdog.cpp @@ -2,74 +2,148 @@ #include <mdk/nrf.h> using namespace Pinetime::Drivers; -void Watchdog::Setup(uint8_t timeoutSeconds) { - NRF_WDT->CONFIG &= ~(WDT_CONFIG_SLEEP_Msk << WDT_CONFIG_SLEEP_Pos); - NRF_WDT->CONFIG |= (WDT_CONFIG_HALT_Run << WDT_CONFIG_SLEEP_Pos); +namespace { + /// The watchdog is always driven by a 32768kHz clock + constexpr uint32_t ClockFrequency = 32768; + /// Write this value in the reload register to reload the watchdog + constexpr uint32_t ReloadValue = 0x6E524635UL; - NRF_WDT->CONFIG &= ~(WDT_CONFIG_HALT_Msk << WDT_CONFIG_HALT_Pos); - NRF_WDT->CONFIG |= (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos); + /// Configures the behaviours (pause or run) of the watchdog while the CPU is sleeping or halted by the debugger + /// + /// @param sleepBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is sleeping + /// @param haltBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is halted by the debugger + void SetBehaviours(Watchdog::SleepBehaviour sleepBehaviour, Watchdog::HaltBehaviour haltBehaviour) { + // NRF_WDT->CONFIG : only the 1st and 4th bits are relevant. + // Bit 0 : Behavior when the CPU is sleeping + // Bit 3 : Behavior when the CPU is halted by the debugger + // O means that the CPU is paused during sleep/halt, 1 means that the watchdog is kept running + NRF_WDT->CONFIG = static_cast<uint32_t>(sleepBehaviour) | static_cast<uint32_t>(haltBehaviour); + } - /* timeout (s) = (CRV + 1) / 32768 */ - // JF : 7500 = 7.5s - uint32_t crv = (((timeoutSeconds * 1000u) << 15u) / 1000) - 1; - NRF_WDT->CRV = crv; + /// Configure the timeout delay of the watchdog (called CRV, Counter Reload Value, in the documentation). + /// + /// @param timeoutSeconds Timeout of the watchdog, expressed in seconds + void SetTimeout(uint8_t timeoutSeconds) { + // According to the documentation: + // Clock = 32768 + // timeout [s] = ( CRV + 1 ) / Clock + // -> CRV = (timeout [s] * Clock) -1 + NRF_WDT->CRV = (timeoutSeconds * ClockFrequency) - 1; + } - /* Enable reload requests */ - NRF_WDT->RREN = (WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos); + /// Enables the first reload register + /// + /// The hardware provides 8 reload registers. To reload the watchdog, all enabled + /// register must be refreshed. + /// + /// This driver only enables the first reload register. + void EnableFirstReloadRegister() { + // RRED (Reload Register Enable) is a bitfield of 8 bits. Each bit represent + // one of the eight reload registers available. + // In this case, we enable only the first one. + NRF_WDT->RREN |= 1; + } - resetReason = ActualResetReason(); -} + /// Returns the reset reason provided by the POWER subsystem + Watchdog::ResetReason GetResetReason() { + /* NRF_POWER->RESETREAS + * -------------------------------------------------------------------------------------------------------------------- * + * Bit | Reason (if bit is set to 1) + * ----|---------------------------------------------------------------------------------------------------------------- * + * 0 | Reset from the pin reset + * 1 | Reset from the watchdog + * 2 | Reset from soft reset + * 3 | Reset from CPU lock-up + * 16 | Reset due to wake up from System OFF mode when wakeup is triggered from DETECT signal from GPIO + * 17 | Reset due to wake up from System OFF mode when wakeup is triggered from ANADETECT signal from LPCOMP + * 18 | Reset due to wake up from System OFF mode when wakeup is triggered from entering into debug interface mode + * 19 | Reset due to wake up from System OFF mode by NFC field detect + * -------------------------------------------------------------------------------------------------------------------- */ + const uint32_t reason = NRF_POWER->RESETREAS; + NRF_POWER->RESETREAS = 0xffffffff; -void Watchdog::Start() { - NRF_WDT->TASKS_START = 1; + uint32_t value = reason & 0x01; // avoid implicit conversion to bool using this temporary variable. + if (value != 0) { + return Watchdog::ResetReason::ResetPin; + } + + value = (reason >> 1u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::Watchdog; + } + + value = (reason >> 2u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::SoftReset; + } + + value = (reason >> 3u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::CpuLockup; + } + + value = (reason >> 16u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::SystemOff; + } + + value = (reason >> 17u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::LpComp; + } + + value = (reason >> 18u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::DebugInterface; + } + + value = (reason >> 19u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::NFC; + } + + return Watchdog::ResetReason::HardReset; + } } -void Watchdog::Kick() { - NRF_WDT->RR[0] = WDT_RR_RR_Reload; +void Watchdog::Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour) { + SetBehaviours(sleepBehaviour, haltBehaviour); + SetTimeout(timeoutSeconds); + EnableFirstReloadRegister(); + + resetReason = ::GetResetReason(); } -Watchdog::ResetReasons Watchdog::ActualResetReason() const { - uint32_t reason = NRF_POWER->RESETREAS; - NRF_POWER->RESETREAS = 0xffffffff; +void Watchdog::Start() { + // Write 1 in the START task to start the watchdog + NRF_WDT->TASKS_START = 1; +} - if (reason & 0x01u) - return ResetReasons::ResetPin; - if ((reason >> 1u) & 0x01u) - return ResetReasons::Watchdog; - if ((reason >> 2u) & 0x01u) - return ResetReasons::SoftReset; - if ((reason >> 3u) & 0x01u) - return ResetReasons::CpuLockup; - if ((reason >> 16u) & 0x01u) - return ResetReasons::SystemOff; - if ((reason >> 17u) & 0x01u) - return ResetReasons::LpComp; - if ((reason) &0x01u) - return ResetReasons::DebugInterface; - if ((reason >> 19u) & 0x01u) - return ResetReasons::NFC; - return ResetReasons::HardReset; +void Watchdog::Reload() { + // Write the reload value 0x6E524635UL to the reload register to reload the watchdog. + // NOTE : This driver enables only the 1st reload register. + NRF_WDT->RR[0] = ReloadValue; } -const char* Watchdog::ResetReasonToString(Watchdog::ResetReasons reason) { +const char* Pinetime::Drivers::ResetReasonToString(Watchdog::ResetReason reason) { switch (reason) { - case ResetReasons::ResetPin: + case Watchdog::ResetReason::ResetPin: return "Reset pin"; - case ResetReasons::Watchdog: + case Watchdog::ResetReason::Watchdog: return "Watchdog"; - case ResetReasons::DebugInterface: + case Watchdog::ResetReason::DebugInterface: return "Debug interface"; - case ResetReasons::LpComp: + case Watchdog::ResetReason::LpComp: return "LPCOMP"; - case ResetReasons::SystemOff: + case Watchdog::ResetReason::SystemOff: return "System OFF"; - case ResetReasons::CpuLockup: + case Watchdog::ResetReason::CpuLockup: return "CPU Lock-up"; - case ResetReasons::SoftReset: + case Watchdog::ResetReason::SoftReset: return "Soft reset"; - case ResetReasons::NFC: + case Watchdog::ResetReason::NFC: return "NFC"; - case ResetReasons::HardReset: + case Watchdog::ResetReason::HardReset: return "Hard reset"; default: return "Unknown"; |
