InfiniTime.git

ref: e5b73212f6addcfdb5e306df63d7135e543c4f8d

src/drivers/Watchdog.cpp


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include "drivers/Watchdog.h"
#include <mdk/nrf.h>
using namespace Pinetime::Drivers;

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;

  /// 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);
  }

  /// 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;
  }

  /// 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 = NRF_WDT->RREN | 1;
  }

  /// 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;

    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::Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour) {
  SetBehaviours(sleepBehaviour, haltBehaviour);
  SetTimeout(timeoutSeconds);
  EnableFirstReloadRegister();

  resetReason = ::GetResetReason();
}

void Watchdog::Start() {
  // Write 1 in the START task to start the watchdog
  NRF_WDT->TASKS_START = 1;
}

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* Pinetime::Drivers::ResetReasonToString(Watchdog::ResetReason reason) {
  switch (reason) {
    case Watchdog::ResetReason::ResetPin:
      return "Reset pin";
    case Watchdog::ResetReason::Watchdog:
      return "Watchdog";
    case Watchdog::ResetReason::DebugInterface:
      return "Debug interface";
    case Watchdog::ResetReason::LpComp:
      return "LPCOMP";
    case Watchdog::ResetReason::SystemOff:
      return "System OFF";
    case Watchdog::ResetReason::CpuLockup:
      return "CPU Lock-up";
    case Watchdog::ResetReason::SoftReset:
      return "Soft reset";
    case Watchdog::ResetReason::NFC:
      return "NFC";
    case Watchdog::ResetReason::HardReset:
      return "Hard reset";
    default:
      return "Unknown";
  }
}