blob: a6da7d29ed5bdd39540c7ada3680808b12fea492 [file] [log] [blame]
/* 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);