Loading...
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 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | /*
* Copyright (c) 2018, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <nrfx_wdt.h>
#include <watchdog.h>
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(wdt_nrfx);
DEVICE_DECLARE(wdt_nrfx);
/* Each activated and not reloaded channel can generate watchdog interrupt.
* Array m_callbacks provides storing callbacks for each channel.
*/
static wdt_callback_t m_callbacks[NRF_WDT_CHANNEL_NUMBER];
/* The m_allocated_channels variable stores number of currently allocated
* and activated watchdog channels.
*/
static u8_t m_allocated_channels;
/* The m_timeout variable stores watchdog timeout value in millisecond units.
* It is used to check whether installing watchdog provide the same timeout
* value in the window configuration.
*/
static u32_t m_timeout;
static int wdt_nrf_setup(struct device *dev, u8_t options)
{
nrf_wdt_behaviour_t behaviour;
ARG_UNUSED(dev);
/* Activate all available options. Run in all cases. */
behaviour = NRF_WDT_BEHAVIOUR_RUN_SLEEP_HALT;
/* Deactivate running in sleep mode. */
if (options & WDT_OPT_PAUSE_IN_SLEEP) {
behaviour &= ~NRF_WDT_BEHAVIOUR_RUN_SLEEP;
}
/* Deactivate running when debugger is attached. */
if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) {
behaviour &= ~NRF_WDT_BEHAVIOUR_RUN_HALT;
}
nrf_wdt_behaviour_set(behaviour);
/* The watchdog timer is driven by the LFCLK clock running at 32768 Hz.
* The timeout value given in milliseconds needs to be converted here
* to watchdog ticks.*/
nrf_wdt_reload_value_set(
(uint32_t)(((uint64_t)m_timeout * 32768U) / 1000));
nrfx_wdt_enable();
return 0;
}
static int wdt_nrf_disable(struct device *dev)
{
/* Started watchdog cannot be stopped on nRF devices. */
ARG_UNUSED(dev);
return -EPERM;
}
static int wdt_nrf_install_timeout(struct device *dev,
const struct wdt_timeout_cfg *cfg)
{
nrfx_err_t err_code;
nrfx_wdt_channel_id channel_id;
ARG_UNUSED(dev);
if (cfg->flags != WDT_FLAG_RESET_SOC) {
return -ENOTSUP;
}
if (cfg->window.min != 0U) {
return -EINVAL;
}
if (m_allocated_channels == 0U) {
/* According to relevant Product Specifications, watchdogs
* in all nRF chips can use reload values (determining
* the timeout) from range 0xF-0xFFFFFFFF given in 32768 Hz
* clock ticks. This makes the allowed range of 0x1-0x07CFFFFF
* in milliseconds. Check if the provided value is within
* this range. */
if ((cfg->window.max == 0U) || (cfg->window.max > 0x07CFFFFF)) {
return -EINVAL;
}
/* Save timeout value from first registered watchdog channel. */
m_timeout = cfg->window.max;
} else if (cfg->window.max != m_timeout) {
return -EINVAL;
}
err_code = nrfx_wdt_channel_alloc(&channel_id);
if (err_code == NRFX_ERROR_NO_MEM) {
return -ENOMEM;
}
if (cfg->callback != NULL) {
m_callbacks[channel_id] = cfg->callback;
}
m_allocated_channels++;
return channel_id;
}
static int wdt_nrf_feed(struct device *dev, int channel_id)
{
ARG_UNUSED(dev);
if (channel_id > m_allocated_channels) {
return -EINVAL;
}
nrfx_wdt_channel_feed((nrfx_wdt_channel_id)channel_id);
return 0;
}
static const struct wdt_driver_api wdt_nrf_api = {
.setup = wdt_nrf_setup,
.disable = wdt_nrf_disable,
.install_timeout = wdt_nrf_install_timeout,
.feed = wdt_nrf_feed,
};
static void wdt_event_handler(void)
{
int i;
for (i = 0; i < m_allocated_channels ; ++i) {
if (nrf_wdt_request_status((nrf_wdt_rr_register_t)i)) {
if (m_callbacks[i]) {
m_callbacks[i](DEVICE_GET(wdt_nrfx), i);
}
}
}
}
static int init_wdt(struct device *dev)
{
nrfx_err_t err_code;
/* Set default values. They will be overwritten by setup function. */
const nrfx_wdt_config_t config = {
.behaviour = NRF_WDT_BEHAVIOUR_RUN_SLEEP_HALT,
.reload_value = 2000
};
ARG_UNUSED(dev);
err_code = nrfx_wdt_init(&config, wdt_event_handler);
if (err_code != NRFX_SUCCESS) {
return -EBUSY;
}
IRQ_CONNECT(DT_NORDIC_NRF_WATCHDOG_WDT_0_IRQ,
DT_NORDIC_NRF_WATCHDOG_WDT_0_IRQ_PRIORITY,
nrfx_isr, nrfx_wdt_irq_handler, 0);
irq_enable(DT_NORDIC_NRF_WATCHDOG_WDT_0_IRQ);
return 0;
}
DEVICE_AND_API_INIT(wdt_nrf, DT_NORDIC_NRF_WATCHDOG_WDT_0_LABEL, init_wdt,
NULL, NULL, PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_nrf_api);
|