drivers: wdt_nrfx: Add multi-instance support

Update the driver to support multiple watchdog instances
and add the corresponding Kconfig options.

Signed-off-by: Karol Lasończyk <karol.lasonczyk@nordicsemi.no>
Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Karol Lasończyk 2019-10-14 12:33:33 +02:00 committed by Carles Cufí
parent b814f8ab4b
commit ec2a401bf3
2 changed files with 97 additions and 61 deletions

View file

@ -10,3 +10,16 @@ config WDT_NRFX
select NRFX_WDT
help
Enable support for nrfx WDT driver for nRF MCU series.
config NRFX_WDT0
bool "Enable WDT0 instance"
depends on HAS_HW_NRF_WDT || HAS_HW_NRF_WDT0
default y
help
Enable support for nrfx WDT instance 0.
config NRFX_WDT1
bool "Enable WDT1 instance"
depends on HAS_HW_NRF_WDT1
help
Enable support for nrfx WDT instance 1.

View file

@ -11,30 +11,32 @@
#include <logging/log.h>
LOG_MODULE_REGISTER(wdt_nrfx);
DEVICE_DECLARE(wdt_nrfx);
struct wdt_nrfx_data {
wdt_callback_t m_callbacks[NRF_WDT_CHANNEL_NUMBER];
u32_t m_timeout;
u8_t m_allocated_channels;
};
/* 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];
struct wdt_nrfx_config {
nrfx_wdt_t wdt;
nrfx_wdt_config_t config;
};
/* The m_allocated_channels variable stores number of currently allocated
* and activated watchdog channels.
*/
static u8_t m_allocated_channels;
static inline struct wdt_nrfx_data *get_dev_data(struct device *dev)
{
return dev->driver_data;
}
static inline const struct wdt_nrfx_config *get_dev_config(struct device *dev)
{
return dev->config->config_info;
}
/* 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;
@ -48,14 +50,16 @@ static int wdt_nrf_setup(struct device *dev, u8_t options)
behaviour &= ~NRF_WDT_BEHAVIOUR_RUN_HALT;
}
nrf_wdt_behaviour_set(behaviour);
nrf_wdt_behaviour_set(get_dev_config(dev)->wdt.p_reg, 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));
get_dev_config(dev)->wdt.p_reg,
(uint32_t)(((uint64_t)get_dev_data(dev)->m_timeout * 32768U)
/ 1000));
nrfx_wdt_enable();
nrfx_wdt_enable(&get_dev_config(dev)->wdt);
return 0;
}
@ -73,8 +77,6 @@ static int wdt_nrf_install_timeout(struct device *dev,
nrfx_err_t err_code;
nrfx_wdt_channel_id channel_id;
ARG_UNUSED(dev);
if (cfg->flags != WDT_FLAG_RESET_SOC) {
return -ENOTSUP;
}
@ -83,7 +85,7 @@ static int wdt_nrf_install_timeout(struct device *dev,
return -EINVAL;
}
if (m_allocated_channels == 0U) {
if (get_dev_data(dev)->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
@ -95,81 +97,102 @@ static int wdt_nrf_install_timeout(struct device *dev,
}
/* Save timeout value from first registered watchdog channel. */
m_timeout = cfg->window.max;
} else if (cfg->window.max != m_timeout) {
get_dev_data(dev)->m_timeout = cfg->window.max;
} else if (cfg->window.max != get_dev_data(dev)->m_timeout) {
return -EINVAL;
}
err_code = nrfx_wdt_channel_alloc(&channel_id);
err_code = nrfx_wdt_channel_alloc(&get_dev_config(dev)->wdt,
&channel_id);
if (err_code == NRFX_ERROR_NO_MEM) {
return -ENOMEM;
}
if (cfg->callback != NULL) {
m_callbacks[channel_id] = cfg->callback;
get_dev_data(dev)->m_callbacks[channel_id] = cfg->callback;
}
m_allocated_channels++;
get_dev_data(dev)->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) {
if (channel_id > get_dev_data(dev)->m_allocated_channels) {
return -EINVAL;
}
nrfx_wdt_channel_feed((nrfx_wdt_channel_id)channel_id);
nrfx_wdt_channel_feed(&get_dev_config(dev)->wdt,
(nrfx_wdt_channel_id)channel_id);
return 0;
}
static const struct wdt_driver_api wdt_nrf_api = {
static const struct wdt_driver_api wdt_nrfx_driver_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)
static void wdt_event_handler(struct device *dev)
{
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);
for (i = 0; i < get_dev_data(dev)->m_allocated_channels; ++i) {
if (nrf_wdt_request_status(get_dev_config(dev)->wdt.p_reg,
(nrf_wdt_rr_register_t)i)) {
if (get_dev_data(dev)->m_callbacks[i]) {
get_dev_data(dev)->m_callbacks[i](dev, 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
};
#define WDT_NRFX_WDT_DEVICE(idx) \
DEVICE_DECLARE(wdt_##idx); \
static void wdt_##idx##_event_handler(void) \
{ \
wdt_event_handler(DEVICE_GET(wdt_##idx)); \
} \
static int wdt_##idx##_init(struct device *dev) \
{ \
nrfx_err_t err_code; \
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_WDT##idx), \
DT_NORDIC_NRF_WATCHDOG_WDT_##idx##_IRQ_0_PRIORITY, \
nrfx_isr, nrfx_wdt_##idx##_irq_handler, 0); \
err_code = nrfx_wdt_init(&get_dev_config(dev)->wdt, \
&get_dev_config(dev)->config, \
wdt_##idx##_event_handler); \
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), \
.config = { \
.behaviour = NRF_WDT_BEHAVIOUR_RUN_SLEEP_HALT, \
.reload_value = 2000, \
} \
}; \
DEVICE_DEFINE(wdt_##idx, DT_NORDIC_NRF_WATCHDOG_WDT_##idx##_LABEL, \
wdt_##idx##_init, \
NULL, \
&wdt_##idx##_data, \
&wdt_##idx##z_config, \
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&wdt_nrfx_driver_api)
ARG_UNUSED(dev);
#ifdef CONFIG_NRFX_WDT0
WDT_NRFX_WDT_DEVICE(0);
#endif
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_0,
DT_NORDIC_NRF_WATCHDOG_WDT_0_IRQ_0_PRIORITY,
nrfx_isr, nrfx_wdt_irq_handler, 0);
irq_enable(DT_NORDIC_NRF_WATCHDOG_WDT_0_IRQ_0);
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);
#ifdef CONFIG_NRFX_WDT1
WDT_NRFX_WDT_DEVICE(1);
#endif