2018-05-14 09:08:54 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018, Nordic Semiconductor ASA
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2023-04-21 12:12:14 +02:00
|
|
|
#include <zephyr/sys/math_extras.h>
|
2018-05-14 09:08:54 +02:00
|
|
|
#include <nrfx_wdt.h>
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/drivers/watchdog.h>
|
2018-05-14 09:08:54 +02:00
|
|
|
|
2018-08-09 11:56:26 +02:00
|
|
|
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/logging/log.h>
|
2022-10-17 10:24:11 +02:00
|
|
|
#include <zephyr/irq.h>
|
2018-08-09 11:56:26 +02:00
|
|
|
LOG_MODULE_REGISTER(wdt_nrfx);
|
2018-05-14 09:08:54 +02:00
|
|
|
|
2019-10-14 12:33:33 +02:00
|
|
|
struct wdt_nrfx_data {
|
|
|
|
wdt_callback_t m_callbacks[NRF_WDT_CHANNEL_NUMBER];
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t m_timeout;
|
|
|
|
uint8_t m_allocated_channels;
|
2019-10-14 12:33:33 +02:00
|
|
|
};
|
2018-05-14 09:08:54 +02:00
|
|
|
|
2019-10-14 12:33:33 +02:00
|
|
|
struct wdt_nrfx_config {
|
2023-11-16 10:56:03 +01:00
|
|
|
nrfx_wdt_t wdt;
|
2019-10-14 12:33:33 +02:00
|
|
|
};
|
2018-05-14 09:08:54 +02:00
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_nrf_setup(const struct device *dev, uint8_t options)
|
2018-05-14 09:08:54 +02:00
|
|
|
{
|
2022-01-19 16:31:04 +01:00
|
|
|
const struct wdt_nrfx_config *config = dev->config;
|
2023-11-16 10:56:03 +01:00
|
|
|
const struct wdt_nrfx_data *data = dev->data;
|
|
|
|
nrfx_err_t err_code;
|
2018-05-14 09:08:54 +02:00
|
|
|
|
2023-11-16 10:56:03 +01:00
|
|
|
nrfx_wdt_config_t wdt_config = {
|
|
|
|
.reload_value = data->m_timeout
|
|
|
|
};
|
2018-05-14 09:08:54 +02:00
|
|
|
|
2023-09-22 09:08:17 +02:00
|
|
|
#if NRF_WDT_HAS_STOP
|
|
|
|
wdt_config.behaviour |= NRF_WDT_BEHAVIOUR_STOP_ENABLE_MASK;
|
|
|
|
#endif
|
|
|
|
|
2023-11-16 10:56:03 +01:00
|
|
|
if (!(options & WDT_OPT_PAUSE_IN_SLEEP)) {
|
|
|
|
wdt_config.behaviour |= NRF_WDT_BEHAVIOUR_RUN_SLEEP_MASK;
|
2018-05-14 09:08:54 +02:00
|
|
|
}
|
|
|
|
|
2023-11-16 10:56:03 +01:00
|
|
|
if (!(options & WDT_OPT_PAUSE_HALTED_BY_DBG)) {
|
|
|
|
wdt_config.behaviour |= NRF_WDT_BEHAVIOUR_RUN_HALT_MASK;
|
2018-05-14 09:08:54 +02:00
|
|
|
}
|
|
|
|
|
2023-11-16 10:56:03 +01:00
|
|
|
err_code = nrfx_wdt_reconfigure(&config->wdt, &wdt_config);
|
|
|
|
|
|
|
|
if (err_code != NRFX_SUCCESS) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2018-05-14 09:08:54 +02:00
|
|
|
|
2022-01-19 16:31:04 +01:00
|
|
|
nrfx_wdt_enable(&config->wdt);
|
2018-05-14 09:08:54 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_nrf_disable(const struct device *dev)
|
2018-05-14 09:08:54 +02:00
|
|
|
{
|
2023-09-22 09:08:17 +02:00
|
|
|
#if NRFX_WDT_HAS_STOP
|
|
|
|
const struct wdt_nrfx_config *config = dev->config;
|
|
|
|
nrfx_err_t err_code;
|
|
|
|
|
|
|
|
err_code = nrfx_wdt_stop(&config->wdt);
|
|
|
|
|
|
|
|
if (err_code != NRFX_SUCCESS) {
|
2024-03-13 14:53:54 +01:00
|
|
|
/* This can only happen if wdt_nrf_setup() is not called first. */
|
|
|
|
return -EFAULT;
|
2023-09-22 09:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
#else
|
2018-05-14 09:08:54 +02:00
|
|
|
ARG_UNUSED(dev);
|
|
|
|
return -EPERM;
|
2023-09-22 09:08:17 +02:00
|
|
|
#endif
|
2018-05-14 09:08:54 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_nrf_install_timeout(const struct device *dev,
|
2018-05-14 09:08:54 +02:00
|
|
|
const struct wdt_timeout_cfg *cfg)
|
|
|
|
{
|
2022-01-19 16:31:04 +01:00
|
|
|
const struct wdt_nrfx_config *config = dev->config;
|
|
|
|
struct wdt_nrfx_data *data = dev->data;
|
2018-05-14 09:08:54 +02:00
|
|
|
nrfx_err_t err_code;
|
|
|
|
nrfx_wdt_channel_id channel_id;
|
|
|
|
|
|
|
|
if (cfg->flags != WDT_FLAG_RESET_SOC) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
if (cfg->window.min != 0U) {
|
2018-05-14 09:08:54 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-19 16:31:04 +01:00
|
|
|
if (data->m_allocated_channels == 0U) {
|
2018-07-18 15:50:41 +02:00
|
|
|
/* 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. */
|
2019-03-27 02:57:45 +01:00
|
|
|
if ((cfg->window.max == 0U) || (cfg->window.max > 0x07CFFFFF)) {
|
2018-07-18 15:50:41 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-05-14 09:08:54 +02:00
|
|
|
/* Save timeout value from first registered watchdog channel. */
|
2022-01-19 16:31:04 +01:00
|
|
|
data->m_timeout = cfg->window.max;
|
|
|
|
} else if (cfg->window.max != data->m_timeout) {
|
2018-05-14 09:08:54 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-19 16:31:04 +01:00
|
|
|
err_code = nrfx_wdt_channel_alloc(&config->wdt,
|
2019-10-14 12:33:33 +02:00
|
|
|
&channel_id);
|
2018-05-14 09:08:54 +02:00
|
|
|
|
|
|
|
if (err_code == NRFX_ERROR_NO_MEM) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->callback != NULL) {
|
2022-01-19 16:31:04 +01:00
|
|
|
data->m_callbacks[channel_id] = cfg->callback;
|
2018-05-14 09:08:54 +02:00
|
|
|
}
|
|
|
|
|
2022-01-19 16:31:04 +01:00
|
|
|
data->m_allocated_channels++;
|
2018-05-14 09:08:54 +02:00
|
|
|
return channel_id;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int wdt_nrf_feed(const struct device *dev, int channel_id)
|
2018-05-14 09:08:54 +02:00
|
|
|
{
|
2022-01-19 16:31:04 +01:00
|
|
|
const struct wdt_nrfx_config *config = dev->config;
|
|
|
|
struct wdt_nrfx_data *data = dev->data;
|
|
|
|
|
2024-03-14 09:14:07 +01:00
|
|
|
if ((channel_id >= data->m_allocated_channels) || (channel_id < 0)) {
|
2018-05-14 09:08:54 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-19 16:31:04 +01:00
|
|
|
nrfx_wdt_channel_feed(&config->wdt,
|
2019-10-14 12:33:33 +02:00
|
|
|
(nrfx_wdt_channel_id)channel_id);
|
2018-05-14 09:08:54 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-14 12:33:33 +02:00
|
|
|
static const struct wdt_driver_api wdt_nrfx_driver_api = {
|
2018-05-14 09:08:54 +02:00
|
|
|
.setup = wdt_nrf_setup,
|
|
|
|
.disable = wdt_nrf_disable,
|
|
|
|
.install_timeout = wdt_nrf_install_timeout,
|
|
|
|
.feed = wdt_nrf_feed,
|
|
|
|
};
|
|
|
|
|
2024-01-08 09:45:46 +01:00
|
|
|
static void wdt_event_handler(const struct device *dev, nrf_wdt_event_t event_type,
|
|
|
|
uint32_t requests, void *p_context)
|
2018-07-18 15:50:41 +02:00
|
|
|
{
|
2024-01-08 09:45:46 +01:00
|
|
|
(void)event_type;
|
|
|
|
(void)p_context;
|
|
|
|
|
2022-01-19 16:31:04 +01:00
|
|
|
struct wdt_nrfx_data *data = dev->data;
|
2023-04-21 12:12:14 +02:00
|
|
|
|
|
|
|
while (requests) {
|
|
|
|
uint8_t i = u32_count_trailing_zeros(requests);
|
|
|
|
|
|
|
|
if (data->m_callbacks[i]) {
|
|
|
|
data->m_callbacks[i](dev, i);
|
2018-07-18 15:50:41 +02:00
|
|
|
}
|
2023-04-21 12:12:14 +02:00
|
|
|
requests &= ~BIT(i);
|
2018-07-18 15:50:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 22:27:19 +02:00
|
|
|
#define WDT(idx) DT_NODELABEL(wdt##idx)
|
|
|
|
|
2019-10-14 12:33:33 +02:00
|
|
|
#define WDT_NRFX_WDT_DEVICE(idx) \
|
2024-01-08 09:45:46 +01:00
|
|
|
static void wdt_##idx##_event_handler(nrf_wdt_event_t event_type, \
|
|
|
|
uint32_t requests, \
|
|
|
|
void *p_context) \
|
2019-10-14 12:33:33 +02:00
|
|
|
{ \
|
2024-01-08 09:45:46 +01:00
|
|
|
wdt_event_handler(DEVICE_DT_GET(WDT(idx)), event_type, \
|
|
|
|
requests, p_context); \
|
2019-10-14 12:33:33 +02:00
|
|
|
} \
|
2020-07-14 17:02:00 +02:00
|
|
|
static int wdt_##idx##_init(const struct device *dev) \
|
2019-10-14 12:33:33 +02:00
|
|
|
{ \
|
2022-01-19 16:31:04 +01:00
|
|
|
const struct wdt_nrfx_config *config = dev->config; \
|
2019-10-14 12:33:33 +02:00
|
|
|
nrfx_err_t err_code; \
|
2020-04-16 22:27:19 +02:00
|
|
|
IRQ_CONNECT(DT_IRQN(WDT(idx)), DT_IRQ(WDT(idx), priority), \
|
2019-10-14 12:33:33 +02:00
|
|
|
nrfx_isr, nrfx_wdt_##idx##_irq_handler, 0); \
|
2022-01-19 16:31:04 +01:00
|
|
|
err_code = nrfx_wdt_init(&config->wdt, \
|
2023-11-16 10:56:03 +01:00
|
|
|
NULL, \
|
2024-01-08 09:45:46 +01:00
|
|
|
wdt_##idx##_event_handler, \
|
|
|
|
NULL); \
|
2019-10-14 12:33:33 +02:00
|
|
|
if (err_code != NRFX_SUCCESS) { \
|
|
|
|
return -EBUSY; \
|
|
|
|
} \
|
|
|
|
return 0; \
|
|
|
|
} \
|
|
|
|
static struct wdt_nrfx_data wdt_##idx##_data = { \
|
|
|
|
.m_timeout = 0, \
|
|
|
|
.m_allocated_channels = 0, \
|
|
|
|
}; \
|
|
|
|
static const struct wdt_nrfx_config wdt_##idx##z_config = { \
|
|
|
|
.wdt = NRFX_WDT_INSTANCE(idx), \
|
|
|
|
}; \
|
2020-12-10 15:49:59 +01:00
|
|
|
DEVICE_DT_DEFINE(WDT(idx), \
|
2019-12-16 15:55:10 +01:00
|
|
|
wdt_##idx##_init, \
|
2021-04-28 12:07:15 +02:00
|
|
|
NULL, \
|
2019-12-16 15:55:10 +01:00
|
|
|
&wdt_##idx##_data, \
|
|
|
|
&wdt_##idx##z_config, \
|
|
|
|
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
|
|
|
&wdt_nrfx_driver_api)
|
2019-10-14 12:33:33 +02:00
|
|
|
|
2023-07-20 10:00:13 +02:00
|
|
|
#ifdef CONFIG_HAS_HW_NRF_WDT0
|
2019-10-14 12:33:33 +02:00
|
|
|
WDT_NRFX_WDT_DEVICE(0);
|
|
|
|
#endif
|
|
|
|
|
2023-07-20 10:00:13 +02:00
|
|
|
#ifdef CONFIG_HAS_HW_NRF_WDT1
|
2019-10-14 12:33:33 +02:00
|
|
|
WDT_NRFX_WDT_DEVICE(1);
|
|
|
|
#endif
|
2023-07-19 15:36:00 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_HAS_HW_NRF_WDT30
|
|
|
|
WDT_NRFX_WDT_DEVICE(30);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_HAS_HW_NRF_WDT31
|
|
|
|
WDT_NRFX_WDT_DEVICE(31);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_HAS_HW_NRF_WDT130
|
|
|
|
WDT_NRFX_WDT_DEVICE(130);
|
|
|
|
#endif
|