/* * Copyright (C) 2024 Vogl Electronic GmbH * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT litex_watchdog #include #include #include #include #include LOG_MODULE_REGISTER(wdt_litex, CONFIG_WDT_LOG_LEVEL); #include struct wdt_litex_data { wdt_callback_t callback; uint32_t timeout; bool reset_soc_mode; bool pause_halted; }; struct wdt_litex_config { uint32_t control_addr; uint32_t cycles_addr; uint32_t cycles_size; uint32_t remaining_addr; uint32_t ev_status_addr; uint32_t ev_pending_addr; uint32_t ev_enable_addr; void (*irq_cfg_func)(void); }; #define CONTROL_FEED_BIT BIT(0) #define CONTROL_ENABLE_BIT BIT(8) #define CONTROL_RESET_BIT BIT(16) #define CONTROL_PAUSE_HALTED_BIT BIT(24) static bool wdt_litex_is_enabled(const struct device *dev) { const struct wdt_litex_config *config = dev->config; return litex_read8(config->control_addr) & BIT(0); } static void wdt_litex_irq_enable(const struct device *dev) { const struct wdt_litex_config *config = dev->config; struct wdt_litex_data *data = dev->data; if (!data->callback) { return; } litex_write8(BIT(0), config->ev_pending_addr); litex_write8(BIT(0), config->ev_enable_addr); } static void wdt_litex_enable(const struct device *dev) { const struct wdt_litex_config *config = dev->config; struct wdt_litex_data *data = dev->data; uint32_t control; if (config->cycles_size <= 4) { litex_write32(k_ms_to_cyc_floor32(data->timeout), config->cycles_addr); } else { litex_write64(k_ms_to_cyc_floor64(data->timeout), config->cycles_addr); } control = CONTROL_FEED_BIT | CONTROL_ENABLE_BIT; if (data->reset_soc_mode) { control |= CONTROL_RESET_BIT; } if (data->pause_halted) { control |= CONTROL_PAUSE_HALTED_BIT; } litex_write32(control, config->control_addr); wdt_litex_irq_enable(dev); } static int wdt_litex_disable(const struct device *dev) { const struct wdt_litex_config *config = dev->config; litex_write8(0, config->ev_enable_addr); if (!wdt_litex_is_enabled(dev)) { return -EFAULT; } litex_write16(CONTROL_ENABLE_BIT, config->control_addr); return 0; } static int wdt_litex_feed(const struct device *dev, int channel_id) { const struct wdt_litex_config *config = dev->config; if (channel_id != 0) { return -EINVAL; } litex_write8(CONTROL_FEED_BIT, config->control_addr); return 0; } static int wdt_litex_setup(const struct device *dev, uint8_t options) { struct wdt_litex_data *data = dev->data; data->pause_halted = !!(options & WDT_OPT_PAUSE_HALTED_BY_DBG); if (options & WDT_OPT_PAUSE_IN_SLEEP) { return -ENOTSUP; } if (wdt_litex_is_enabled(dev)) { return -EBUSY; } wdt_litex_enable(dev); wdt_litex_feed(dev, 0); return 0; } static int wdt_litex_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg) { const struct wdt_litex_config *config = dev->config; struct wdt_litex_data *data = dev->data; if (cfg->window.min != 0U || cfg->window.max == 0U) { return -EINVAL; } if (cfg->window.max > (config->cycles_size <= 4 ? k_cyc_to_ms_floor32(UINT32_MAX) : k_cyc_to_ms_floor64(UINT64_MAX))) { return -EINVAL; } if (wdt_litex_is_enabled(dev)) { return -EBUSY; } data->timeout = cfg->window.max; data->callback = cfg->callback; /* Set mode of watchdog and callback */ switch (cfg->flags) { case WDT_FLAG_RESET_SOC: LOG_DBG("Configuring reset SOC mode"); data->reset_soc_mode = true; break; case WDT_FLAG_RESET_NONE: LOG_DBG("Configuring non-reset mode"); data->reset_soc_mode = false; break; default: LOG_ERR("Unsupported watchdog config flag"); return -EINVAL; } return 0; } static void wdt_litex_isr(void *arg) { const struct device *dev = (const struct device *)arg; const struct wdt_litex_config *config = dev->config; struct wdt_litex_data *data = dev->data; unsigned int key = irq_lock(); if (data->callback) { data->callback(dev, 0); } litex_write8(BIT(0), config->ev_pending_addr); irq_unlock(key); } static int wdt_litex_init(const struct device *dev) { const struct wdt_litex_config *const config = dev->config; config->irq_cfg_func(); #ifndef CONFIG_WDT_DISABLE_AT_BOOT wdt_litex_enable(dev); #endif return 0; } static const struct wdt_driver_api wdt_api = { .setup = wdt_litex_setup, .disable = wdt_litex_disable, .install_timeout = wdt_litex_install_timeout, .feed = wdt_litex_feed, }; #define LITEX_WDT_INIT(n) \ static void wdt_litex_cfg_func_##n(void); \ \ static struct wdt_litex_data wdt_litex_data##n; \ static struct wdt_litex_config wdt_litex_config##n = { \ .control_addr = DT_INST_REG_ADDR_BY_NAME(n, control), \ .cycles_addr = DT_INST_REG_ADDR_BY_NAME(n, cycles), \ .cycles_size = DT_INST_REG_SIZE_BY_NAME(n, cycles), \ .remaining_addr = DT_INST_REG_ADDR_BY_NAME(n, remaining), \ .ev_status_addr = DT_INST_REG_ADDR_BY_NAME(n, ev_status), \ .ev_pending_addr = DT_INST_REG_ADDR_BY_NAME(n, ev_pending), \ .ev_enable_addr = DT_INST_REG_ADDR_BY_NAME(n, ev_enable), \ .irq_cfg_func = wdt_litex_cfg_func_##n, \ }; \ \ DEVICE_DT_INST_DEFINE(n, wdt_litex_init, NULL, &wdt_litex_data##n, &wdt_litex_config##n, \ PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_api) \ \ static void wdt_litex_cfg_func_##n(void) \ { \ IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), wdt_litex_isr, \ DEVICE_DT_INST_GET(n), 0); \ irq_enable(DT_INST_IRQN(n)); \ } DT_INST_FOREACH_STATUS_OKAY(LITEX_WDT_INIT)