diff --git a/drivers/watchdog/CMakeLists.txt b/drivers/watchdog/CMakeLists.txt index b7f3c3bd0de..158b112d0f5 100644 --- a/drivers/watchdog/CMakeLists.txt +++ b/drivers/watchdog/CMakeLists.txt @@ -4,3 +4,4 @@ zephyr_sources_ifdef(CONFIG_WDOG_CMSDK_APB wdog_cmsdk_apb.c) zephyr_sources_ifdef(CONFIG_WDT_SAM wdt_sam.c) zephyr_sources_ifdef(CONFIG_WDT_ESP32 wdt_esp32.c) zephyr_sources_ifdef(CONFIG_WDT_SAM0 wdt_sam0.c) +zephyr_sources_ifdef(CONFIG_WDT_NRFX wdt_nrfx.c) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 562626ea6ff..a09fc5e51e7 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -57,4 +57,6 @@ source "drivers/watchdog/Kconfig.esp32" source "drivers/watchdog/Kconfig.sam0" +source "drivers/watchdog/Kconfig.nrfx" + endif diff --git a/drivers/watchdog/Kconfig.nrfx b/drivers/watchdog/Kconfig.nrfx new file mode 100644 index 00000000000..61325959ccf --- /dev/null +++ b/drivers/watchdog/Kconfig.nrfx @@ -0,0 +1,14 @@ +# Kconfig - nrfx WDT support +# +# Copyright (c) 2018, Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +menuconfig WDT_NRFX + bool "nRF WDT nrfx driver" + depends on SOC_FAMILY_NRF && CLOCK_CONTROL_NRF5 + select HAS_DTS_WDT + select NRFX_WDT + default n + help + Enable support for nrfx WDT driver for nRF MCU series. diff --git a/drivers/watchdog/wdt_nrfx.c b/drivers/watchdog/wdt_nrfx.c new file mode 100644 index 00000000000..7300930debf --- /dev/null +++ b/drivers/watchdog/wdt_nrfx.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define SYS_LOG_DOMAIN "wdt_nrfx" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_WDT_LEVEL +#include + +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 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 void wdt_nrf_isr(struct device *dev) +{ + ARG_UNUSED(dev); + /* We need to implement our own interrupt handler until nrfx one will + * be fixed. + * Clearing events also clears the reload register and has to be done + * AFTER event handler. + */ + wdt_event_handler(); + nrf_wdt_event_clear(NRF_WDT_EVENT_TIMEOUT); +} + +static int wdt_nrf_setup(struct device *dev, u8_t options) +{ + nrfx_wdt_config_t config; + nrfx_err_t err_code; + + ARG_UNUSED(dev); + + if (!nrf_clock_lf_is_running()) { + return -ENOTSUP; + } + + /* Activate all available options. Run in all cases. */ + config.behaviour = NRF_WDT_BEHAVIOUR_RUN_SLEEP_HALT; + + /* Deactivate running in sleep mode. */ + if (options & WDT_OPT_PAUSE_IN_SLEEP) { + config.behaviour &= ~NRF_WDT_BEHAVIOUR_RUN_SLEEP; + } + + /* Deactivate running when debugger is attached. */ + if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) { + config.behaviour &= ~NRF_WDT_BEHAVIOUR_RUN_HALT; + } + + config.reload_value = m_timeout; + + err_code = nrfx_wdt_init(&config, wdt_event_handler); + if (err_code != NRFX_SUCCESS) { + return -EBUSY; + } + + IRQ_CONNECT(CONFIG_WDT_NRF_IRQ, + CONFIG_WDT_NRF_IRQ_PRI, wdt_nrf_isr, + DEVICE_GET(wdt_nrfx), 0); + irq_enable(CONFIG_WDT_NRF_IRQ); + + 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 != 0) { + return -EINVAL; + } + + if (m_allocated_channels == 0) { + /* 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 void wdt_nrf_enable(struct device *dev) +{ + ARG_UNUSED(dev); + /* Deprecated function. No implementation needed. */ + SYS_LOG_ERR("Function not implemented!"); +} + +static int wdt_nrf_set_config(struct device *dev, struct wdt_config *config) +{ + ARG_UNUSED(dev); + ARG_UNUSED(config); + /* Deprecated function. No implementation needed. */ + SYS_LOG_ERR("Function not implemented!"); + return 0; +} + +static void wdt_nrf_get_config(struct device *dev, struct wdt_config *config) +{ + ARG_UNUSED(dev); + ARG_UNUSED(config); + /* Deprecated function. No implementation needed. */ + SYS_LOG_ERR("Function not implemented!"); +} + +static void wdt_nrf_reload(struct device *dev) +{ + ARG_UNUSED(dev); + /* Deprecated function. No implementation needed. */ + SYS_LOG_ERR("Function not implemented!"); +} + +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, + .enable = wdt_nrf_enable, + .get_config = wdt_nrf_get_config, + .set_config = wdt_nrf_set_config, + .reload = wdt_nrf_reload, +}; + +static int init_wdt(struct device *dev) +{ + ARG_UNUSED(dev); + return 0; +} + +DEVICE_AND_API_INIT(wdt_nrf, CONFIG_WDT_0_NAME, init_wdt, + NULL, NULL, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_nrf_api); diff --git a/ext/hal/nordic/Kconfig b/ext/hal/nordic/Kconfig index 56fab3f7b00..88532c55911 100644 --- a/ext/hal/nordic/Kconfig +++ b/ext/hal/nordic/Kconfig @@ -18,3 +18,6 @@ config NRFX_SPIM config NRFX_SPIS bool + +config NRFX_WDT + bool diff --git a/ext/hal/nordic/nrfx_config_nrf51.h b/ext/hal/nordic/nrfx_config_nrf51.h index cb65f6d894c..2fdb1cf4512 100644 --- a/ext/hal/nordic/nrfx_config_nrf51.h +++ b/ext/hal/nordic/nrfx_config_nrf51.h @@ -1504,8 +1504,8 @@ // NRFX_WDT_ENABLED - nrfx_wdt - WDT peripheral driver //========================================================== -#ifndef NRFX_WDT_ENABLED -#define NRFX_WDT_ENABLED 0 +#ifdef CONFIG_NRFX_WDT +#define NRFX_WDT_ENABLED 1 #endif // NRFX_WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode diff --git a/ext/hal/nordic/nrfx_config_nrf52832.h b/ext/hal/nordic/nrfx_config_nrf52832.h index 2a09fee48df..1eb8e65e2da 100644 --- a/ext/hal/nordic/nrfx_config_nrf52832.h +++ b/ext/hal/nordic/nrfx_config_nrf52832.h @@ -2806,8 +2806,8 @@ // NRFX_WDT_ENABLED - nrfx_wdt - WDT peripheral driver //========================================================== -#ifndef NRFX_WDT_ENABLED -#define NRFX_WDT_ENABLED 0 +#ifdef CONFIG_NRFX_WDT +#define NRFX_WDT_ENABLED 1 #endif // NRFX_WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode diff --git a/ext/hal/nordic/nrfx_config_nrf52840.h b/ext/hal/nordic/nrfx_config_nrf52840.h index 764f40346c0..8ddd16f5f59 100644 --- a/ext/hal/nordic/nrfx_config_nrf52840.h +++ b/ext/hal/nordic/nrfx_config_nrf52840.h @@ -2901,8 +2901,8 @@ // NRFX_WDT_ENABLED - nrfx_wdt - WDT peripheral driver //========================================================== -#ifndef NRFX_WDT_ENABLED -#define NRFX_WDT_ENABLED 0 +#ifdef CONFIG_NRFX_WDT +#define NRFX_WDT_ENABLED 1 #endif // NRFX_WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode