| /* |
| * Copyright (c) 2016 Linaro Limited |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT arm_cmsdk_watchdog |
| |
| /** |
| * @brief Driver for CMSDK APB Watchdog. |
| */ |
| |
| #include <errno.h> |
| #include <soc.h> |
| #include <zephyr/arch/arm/aarch32/nmi.h> |
| #include <zephyr/drivers/watchdog.h> |
| #include <zephyr/sys/printk.h> |
| #include <zephyr/sys/reboot.h> |
| |
| struct wdog_cmsdk_apb { |
| /* offset: 0x000 (r/w) watchdog load register */ |
| volatile uint32_t load; |
| /* offset: 0x004 (r/ ) watchdog value register */ |
| volatile uint32_t value; |
| /* offset: 0x008 (r/w) watchdog control register */ |
| volatile uint32_t ctrl; |
| /* offset: 0x00c ( /w) watchdog clear interrupt register */ |
| volatile uint32_t intclr; |
| /* offset: 0x010 (r/ ) watchdog raw interrupt status register */ |
| volatile uint32_t rawintstat; |
| /* offset: 0x014 (r/ ) watchdog interrupt status register */ |
| volatile uint32_t maskintstat; |
| volatile uint32_t reserved0[762]; |
| /* offset: 0xc00 (r/w) watchdog lock register */ |
| volatile uint32_t lock; |
| volatile uint32_t reserved1[191]; |
| /* offset: 0xf00 (r/w) watchdog integration test control register */ |
| volatile uint32_t itcr; |
| /* offset: 0xf04 ( /w) watchdog integration test output set register */ |
| volatile uint32_t itop; |
| }; |
| |
| #define CMSDK_APB_WDOG_LOAD (0xFFFFFFFF << 0) |
| #define CMSDK_APB_WDOG_RELOAD (0xE4E1C00 << 0) |
| #define CMSDK_APB_WDOG_VALUE (0xFFFFFFFF << 0) |
| #define CMSDK_APB_WDOG_CTRL_RESEN (0x1 << 1) |
| #define CMSDK_APB_WDOG_CTRL_INTEN (0x1 << 0) |
| #define CMSDK_APB_WDOG_INTCLR (0x1 << 0) |
| #define CMSDK_APB_WDOG_RAWINTSTAT (0x1 << 0) |
| #define CMSDK_APB_WDOG_MASKINTSTAT (0x1 << 0) |
| #define CMSDK_APB_WDOG_LOCK (0x1 << 0) |
| #define CMSDK_APB_WDOG_INTEGTESTEN (0x1 << 0) |
| #define CMSDK_APB_WDOG_INTEGTESTOUTSET (0x1 << 1) |
| |
| /* |
| * Value written to the LOCK register to lock or unlock the write access |
| * to all of the registers of the watchdog (except the LOCK register) |
| */ |
| #define CMSDK_APB_WDOG_UNLOCK_VALUE (0x1ACCE551) |
| #define CMSDK_APB_WDOG_LOCK_VALUE (0x2BDDF662) |
| |
| #define WDOG_STRUCT \ |
| ((volatile struct wdog_cmsdk_apb *)(DT_INST_REG_ADDR(0))) |
| |
| /* Keep reference of the device to pass it to the callback */ |
| const struct device *wdog_r; |
| |
| /* watchdog reload value in sec */ |
| static unsigned int reload_s = CMSDK_APB_WDOG_RELOAD; |
| static uint8_t flags; |
| |
| static void (*user_cb)(const struct device *dev, int channel_id); |
| |
| static void wdog_cmsdk_apb_unlock(const struct device *dev) |
| { |
| volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT; |
| |
| ARG_UNUSED(dev); |
| |
| wdog->lock = CMSDK_APB_WDOG_UNLOCK_VALUE; |
| } |
| |
| static int wdog_cmsdk_apb_setup(const struct device *dev, uint8_t options) |
| { |
| volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT; |
| |
| ARG_UNUSED(dev); |
| ARG_UNUSED(options); |
| |
| /* Start the watchdog counter with INTEN bit */ |
| wdog->ctrl = (CMSDK_APB_WDOG_CTRL_RESEN | CMSDK_APB_WDOG_CTRL_INTEN); |
| |
| return 0; |
| } |
| |
| static int wdog_cmsdk_apb_disable(const struct device *dev) |
| { |
| volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT; |
| |
| ARG_UNUSED(dev); |
| |
| /* Stop the watchdog counter with INTEN bit */ |
| wdog->ctrl = ~(CMSDK_APB_WDOG_CTRL_RESEN | CMSDK_APB_WDOG_CTRL_INTEN); |
| |
| return 0; |
| } |
| |
| static int wdog_cmsdk_apb_install_timeout(const struct device *dev, |
| const struct wdt_timeout_cfg *config) |
| { |
| volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT; |
| |
| ARG_UNUSED(dev); |
| |
| /* Reload value */ |
| reload_s = config->window.max * |
| DT_INST_PROP_BY_PHANDLE(0, clocks, clock_frequency); |
| flags = config->flags; |
| |
| wdog->load = reload_s; |
| |
| /* Configure only the callback */ |
| user_cb = config->callback; |
| |
| return 0; |
| } |
| |
| static int wdog_cmsdk_apb_feed(const struct device *dev, int channel_id) |
| { |
| volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT; |
| |
| ARG_UNUSED(dev); |
| ARG_UNUSED(channel_id); |
| |
| /* Clear the interrupt */ |
| wdog->intclr = CMSDK_APB_WDOG_INTCLR; |
| |
| /* Reload */ |
| wdog->load = reload_s; |
| |
| return 0; |
| } |
| |
| static const struct wdt_driver_api wdog_cmsdk_apb_api = { |
| .setup = wdog_cmsdk_apb_setup, |
| .disable = wdog_cmsdk_apb_disable, |
| .install_timeout = wdog_cmsdk_apb_install_timeout, |
| .feed = wdog_cmsdk_apb_feed, |
| }; |
| |
| #ifdef CONFIG_RUNTIME_NMI |
| |
| static int wdog_cmsdk_apb_has_fired(void) |
| { |
| volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT; |
| |
| return (wdog->maskintstat & CMSDK_APB_WDOG_MASKINTSTAT) != 0U; |
| } |
| |
| static void wdog_cmsdk_apb_isr(void) |
| { |
| /* |
| * Check if the watchdog was the reason of the NMI interrupt |
| * and if not, exit immediately |
| */ |
| if (!wdog_cmsdk_apb_has_fired()) { |
| printk("NMI received! Rebooting...\n"); |
| /* In ARM implementation sys_reboot ignores the parameter */ |
| sys_reboot(0); |
| } else { |
| if (user_cb != NULL) { |
| user_cb(wdog_r, 0); |
| } |
| } |
| } |
| #endif /* CONFIG_RUNTIME_NMI */ |
| |
| static int wdog_cmsdk_apb_init(const struct device *dev) |
| { |
| volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT; |
| |
| wdog_r = dev; |
| |
| /* unlock access to configuration registers */ |
| wdog_cmsdk_apb_unlock(dev); |
| |
| /* set default reload value */ |
| wdog->load = reload_s; |
| |
| #ifdef CONFIG_RUNTIME_NMI |
| /* Configure the interrupts */ |
| z_arm_nmi_set_handler(wdog_cmsdk_apb_isr); |
| #endif |
| |
| #ifdef CONFIG_WDOG_CMSDK_APB_START_AT_BOOT |
| wdog_cmsdk_apb_setup(dev, 0); |
| #endif |
| |
| return 0; |
| } |
| |
| DEVICE_DT_INST_DEFINE(0, |
| wdog_cmsdk_apb_init, |
| NULL, |
| NULL, NULL, |
| PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &wdog_cmsdk_apb_api); |