| /* wdt_xec.c - Microchip XEC watchdog driver */ |
| |
| #define DT_DRV_COMPAT microchip_xec_watchdog |
| |
| /* |
| * Copyright (c) 2019 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/irq.h> |
| #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(wdt_mchp_xec); |
| |
| #include <zephyr/drivers/watchdog.h> |
| #include <soc.h> |
| #include <errno.h> |
| |
| BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, |
| "add exactly one wdog node to the devicetree"); |
| |
| struct wdt_xec_config { |
| struct wdt_regs *regs; |
| uint8_t girq; |
| uint8_t girq_pos; |
| }; |
| |
| struct wdt_xec_data { |
| wdt_callback_t cb; |
| bool timeout_installed; |
| }; |
| |
| static int wdt_xec_setup(const struct device *dev, uint8_t options) |
| { |
| struct wdt_xec_config const *cfg = dev->config; |
| struct wdt_xec_data *data = dev->data; |
| struct wdt_regs *regs = cfg->regs; |
| |
| if (regs->CTRL & MCHP_WDT_CTRL_EN) { |
| return -EBUSY; |
| } |
| |
| if (!data->timeout_installed) { |
| LOG_ERR("No valid WDT timeout installed"); |
| return -EINVAL; |
| } |
| |
| if (options & WDT_OPT_PAUSE_IN_SLEEP) { |
| LOG_WRN("WDT_OPT_PAUSE_IN_SLEEP is not supported"); |
| return -ENOTSUP; |
| } |
| |
| if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) { |
| regs->CTRL |= MCHP_WDT_CTRL_JTAG_STALL_EN; |
| } else { |
| regs->CTRL &= ~MCHP_WDT_CTRL_JTAG_STALL_EN; |
| } |
| |
| regs->CTRL |= MCHP_WDT_CTRL_EN; |
| |
| LOG_DBG("WDT Setup and enabled"); |
| |
| return 0; |
| } |
| |
| static int wdt_xec_disable(const struct device *dev) |
| { |
| struct wdt_xec_config const *cfg = dev->config; |
| struct wdt_xec_data *data = dev->data; |
| struct wdt_regs *regs = cfg->regs; |
| |
| if (!(regs->CTRL & MCHP_WDT_CTRL_EN)) { |
| return -EALREADY; |
| } |
| |
| regs->CTRL &= ~MCHP_WDT_CTRL_EN; |
| data->timeout_installed = false; |
| |
| LOG_DBG("WDT Disabled"); |
| |
| return 0; |
| } |
| |
| static int wdt_xec_install_timeout(const struct device *dev, |
| const struct wdt_timeout_cfg *config) |
| { |
| struct wdt_xec_config const *cfg = dev->config; |
| struct wdt_xec_data *data = dev->data; |
| struct wdt_regs *regs = cfg->regs; |
| |
| if (regs->CTRL & MCHP_WDT_CTRL_EN) { |
| return -EBUSY; |
| } |
| |
| if (config->window.min > 0U) { |
| data->timeout_installed = false; |
| return -EINVAL; |
| } |
| |
| regs->LOAD = 0; |
| |
| data->cb = config->callback; |
| if (data->cb) { |
| regs->CTRL |= MCHP_WDT_CTRL_MODE_IRQ; |
| regs->IEN |= MCHP_WDT_IEN_EVENT_IRQ_EN; |
| |
| LOG_DBG("WDT callback enabled"); |
| } else { |
| /* Setting WDT_FLAG_RESET_SOC or not will have no effect: |
| * even after the cb, if anything is done, SoC will reset |
| */ |
| regs->CTRL &= ~MCHP_WDT_CTRL_MODE_IRQ; |
| regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN; |
| |
| LOG_DBG("WDT Reset enabled"); |
| } |
| |
| /* Since it almost takes 1ms to decrement the load register |
| * (See datasheet 18.6.1.4: 33/32.768 KHz = 1.007ms) |
| * Let's use the given window directly. |
| */ |
| regs->LOAD = config->window.max; |
| |
| data->timeout_installed = true; |
| |
| return 0; |
| } |
| |
| static int wdt_xec_feed(const struct device *dev, int channel_id) |
| { |
| struct wdt_xec_config const *cfg = dev->config; |
| struct wdt_regs *regs = cfg->regs; |
| |
| ARG_UNUSED(dev); |
| ARG_UNUSED(channel_id); |
| |
| if (!(regs->CTRL & MCHP_WDT_CTRL_EN)) { |
| return -EINVAL; |
| } |
| |
| LOG_DBG("WDT Kicking"); |
| |
| regs->KICK = 1; |
| |
| return 0; |
| } |
| |
| static void wdt_xec_isr(const struct device *dev) |
| { |
| struct wdt_xec_config const *cfg = dev->config; |
| struct wdt_xec_data *data = dev->data; |
| struct wdt_regs *regs = cfg->regs; |
| |
| LOG_DBG("WDT ISR"); |
| |
| if (data->cb) { |
| data->cb(dev, 0); |
| } |
| |
| #ifdef CONFIG_SOC_SERIES_MEC172X |
| mchp_soc_ecia_girq_src_clr(cfg->girq, cfg->girq_pos); |
| #else |
| MCHP_GIRQ_SRC(MCHP_WDT_GIRQ) = BIT(cfg->girq_pos); |
| #endif |
| regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN; |
| } |
| |
| static const struct wdt_driver_api wdt_xec_api = { |
| .setup = wdt_xec_setup, |
| .disable = wdt_xec_disable, |
| .install_timeout = wdt_xec_install_timeout, |
| .feed = wdt_xec_feed, |
| }; |
| |
| static int wdt_xec_init(const struct device *dev) |
| { |
| struct wdt_xec_config const *cfg = dev->config; |
| |
| if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) { |
| wdt_xec_disable(dev); |
| } |
| |
| #ifdef CONFIG_SOC_SERIES_MEC172X |
| mchp_soc_ecia_girq_src_en(cfg->girq, cfg->girq_pos); |
| #else |
| MCHP_GIRQ_ENSET(MCHP_WDT_GIRQ) = BIT(cfg->girq_pos); |
| #endif |
| |
| IRQ_CONNECT(DT_INST_IRQN(0), |
| DT_INST_IRQ(0, priority), |
| wdt_xec_isr, DEVICE_DT_INST_GET(0), 0); |
| irq_enable(DT_INST_IRQN(0)); |
| |
| return 0; |
| } |
| |
| static const struct wdt_xec_config wdt_xec_config_0 = { |
| .regs = (struct wdt_regs *)(DT_INST_REG_ADDR(0)), |
| .girq = DT_INST_PROP_BY_IDX(0, girqs, 0), |
| .girq_pos = DT_INST_PROP_BY_IDX(0, girqs, 1), |
| }; |
| |
| static struct wdt_xec_data wdt_xec_dev_data; |
| |
| DEVICE_DT_INST_DEFINE(0, wdt_xec_init, NULL, |
| &wdt_xec_dev_data, &wdt_xec_config_0, |
| PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &wdt_xec_api); |