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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | /*
* Copyright (C) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT espressif_esp32_watchdog
/* Include esp-idf headers first to avoid redefining BIT() macro */
#include <soc/rtc_cntl_reg.h>
#include <soc/timer_group_reg.h>
#include <hal/mwdt_ll.h>
#include <hal/wdt_hal.h>
#include <string.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/drivers/clock_control.h>
#ifndef CONFIG_SOC_SERIES_ESP32C3
#include <zephyr/drivers/interrupt_controller/intc_esp32.h>
#else
#include <zephyr/drivers/interrupt_controller/intc_esp32c3.h>
#endif
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(wdt_esp32, CONFIG_WDT_LOG_LEVEL);
#ifdef CONFIG_SOC_SERIES_ESP32C3
#define ISR_HANDLER isr_handler_t
#else
#define ISR_HANDLER intr_handler_t
#endif
#define MWDT_TICK_PRESCALER 40000
#define MWDT_TICKS_PER_US 500
struct wdt_esp32_data {
wdt_hal_context_t hal;
uint32_t timeout;
wdt_stage_action_t mode;
wdt_callback_t callback;
};
struct wdt_esp32_config {
wdt_inst_t wdt_inst;
const struct device *clock_dev;
const clock_control_subsys_t clock_subsys;
void (*connect_irq)(void);
int irq_source;
};
static inline void wdt_esp32_seal(const struct device *dev)
{
struct wdt_esp32_data *data = dev->data;
wdt_hal_write_protect_enable(&data->hal);
}
static inline void wdt_esp32_unseal(const struct device *dev)
{
struct wdt_esp32_data *data = dev->data;
wdt_hal_write_protect_disable(&data->hal);
}
static void wdt_esp32_enable(const struct device *dev)
{
struct wdt_esp32_data *data = dev->data;
wdt_esp32_unseal(dev);
wdt_hal_enable(&data->hal);
wdt_esp32_seal(dev);
}
static int wdt_esp32_disable(const struct device *dev)
{
struct wdt_esp32_data *data = dev->data;
wdt_esp32_unseal(dev);
wdt_hal_disable(&data->hal);
wdt_esp32_seal(dev);
return 0;
}
static void wdt_esp32_isr(void *arg);
static int wdt_esp32_feed(const struct device *dev, int channel_id)
{
struct wdt_esp32_data *data = dev->data;
wdt_esp32_unseal(dev);
wdt_hal_feed(&data->hal);
wdt_esp32_seal(dev);
return 0;
}
static int wdt_esp32_set_config(const struct device *dev, uint8_t options)
{
struct wdt_esp32_data *data = dev->data;
wdt_esp32_unseal(dev);
wdt_hal_config_stage(&data->hal, WDT_STAGE0, data->timeout, WDT_STAGE_ACTION_INT);
wdt_hal_config_stage(&data->hal, WDT_STAGE1, data->timeout, data->mode);
wdt_esp32_enable(dev);
wdt_esp32_seal(dev);
wdt_esp32_feed(dev, 0);
return 0;
}
static int wdt_esp32_install_timeout(const struct device *dev,
const struct wdt_timeout_cfg *cfg)
{
struct wdt_esp32_data *data = dev->data;
if (cfg->window.min != 0U || cfg->window.max == 0U) {
return -EINVAL;
}
data->timeout = cfg->window.max;
data->callback = cfg->callback;
/* Set mode of watchdog and callback */
switch (cfg->flags) {
case WDT_FLAG_RESET_SOC:
data->mode = WDT_STAGE_ACTION_RESET_SYSTEM;
LOG_DBG("Configuring reset SOC mode");
break;
case WDT_FLAG_RESET_CPU_CORE:
data->mode = WDT_STAGE_ACTION_RESET_CPU;
LOG_DBG("Configuring reset CPU mode");
break;
case WDT_FLAG_RESET_NONE:
data->mode = WDT_STAGE_ACTION_OFF;
LOG_DBG("Configuring non-reset mode");
break;
default:
LOG_ERR("Unsupported watchdog config flag");
return -EINVAL;
}
return 0;
}
static int wdt_esp32_init(const struct device *dev)
{
const struct wdt_esp32_config *const config = dev->config;
struct wdt_esp32_data *data = dev->data;
if (!device_is_ready(config->clock_dev)) {
LOG_ERR("clock control device not ready");
return -ENODEV;
}
clock_control_on(config->clock_dev, config->clock_subsys);
wdt_hal_init(&data->hal, config->wdt_inst, MWDT_TICK_PRESCALER, true);
esp_intr_alloc(config->irq_source,
0,
(ISR_HANDLER)wdt_esp32_isr,
(void *)dev,
NULL);
#ifndef CONFIG_WDT_DISABLE_AT_BOOT
wdt_esp32_enable(dev);
#endif
return 0;
}
static const struct wdt_driver_api wdt_api = {
.setup = wdt_esp32_set_config,
.disable = wdt_esp32_disable,
.install_timeout = wdt_esp32_install_timeout,
.feed = wdt_esp32_feed
};
#define ESP32_WDT_INIT(idx) \
static struct wdt_esp32_data wdt##idx##_data; \
static struct wdt_esp32_config wdt_esp32_config##idx = { \
.wdt_inst = WDT_MWDT##idx, \
.irq_source = DT_IRQN(DT_NODELABEL(wdt##idx)), \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(idx, offset), \
}; \
\
DEVICE_DT_INST_DEFINE(idx, \
wdt_esp32_init, \
NULL, \
&wdt##idx##_data, \
&wdt_esp32_config##idx, \
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&wdt_api)
static void wdt_esp32_isr(void *arg)
{
const struct device *dev = (const struct device *)arg;
const struct wdt_esp32_config *config = dev->config;
struct wdt_esp32_data *data = dev->data;
if (data->callback) {
data->callback(dev, 0);
}
wdt_hal_handle_intr(&data->hal);
}
#if DT_NODE_HAS_STATUS(DT_NODELABEL(wdt0), okay)
ESP32_WDT_INIT(0);
#endif
#if DT_NODE_HAS_STATUS(DT_NODELABEL(wdt1), okay)
ESP32_WDT_INIT(1);
#endif
|