1 /*
2  * Copyright (C) 2020, NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_imx_wdog
8 
9 #include <zephyr/drivers/watchdog.h>
10 #include <zephyr/sys_clock.h>
11 #include <fsl_wdog.h>
12 
13 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
14 #include <zephyr/logging/log.h>
15 #include <zephyr/irq.h>
16 LOG_MODULE_REGISTER(wdt_mcux_wdog);
17 
18 #define WDOG_TMOUT_SEC(x)  (((x * 2) / MSEC_PER_SEC) - 1)
19 
20 struct mcux_wdog_config {
21 	WDOG_Type *base;
22 	void (*irq_config_func)(const struct device *dev);
23 };
24 
25 struct mcux_wdog_data {
26 	wdt_callback_t callback;
27 	wdog_config_t wdog_config;
28 	bool timeout_valid;
29 };
30 
mcux_wdog_setup(const struct device * dev,uint8_t options)31 static int mcux_wdog_setup(const struct device *dev, uint8_t options)
32 {
33 	const struct mcux_wdog_config *config = dev->config;
34 	struct mcux_wdog_data *data = dev->data;
35 	WDOG_Type *base = config->base;
36 
37 	if (!data->timeout_valid) {
38 		LOG_ERR("No valid timeouts installed");
39 		return -EINVAL;
40 	}
41 
42 	data->wdog_config.workMode.enableStop =
43 		(options & WDT_OPT_PAUSE_IN_SLEEP) == 0U;
44 
45 	data->wdog_config.workMode.enableDebug =
46 		(options & WDT_OPT_PAUSE_HALTED_BY_DBG) == 0U;
47 
48 	WDOG_Init(base, &data->wdog_config);
49 	LOG_DBG("Setup the watchdog");
50 
51 	return 0;
52 }
53 
mcux_wdog_disable(const struct device * dev)54 static int mcux_wdog_disable(const struct device *dev)
55 {
56 	const struct mcux_wdog_config *config = dev->config;
57 	struct mcux_wdog_data *data = dev->data;
58 	WDOG_Type *base = config->base;
59 
60 	WDOG_Deinit(base);
61 	data->timeout_valid = false;
62 	LOG_DBG("Disabled the watchdog");
63 
64 	return 0;
65 }
66 
mcux_wdog_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)67 static int mcux_wdog_install_timeout(const struct device *dev,
68 				     const struct wdt_timeout_cfg *cfg)
69 {
70 	struct mcux_wdog_data *data = dev->data;
71 
72 	if (data->timeout_valid) {
73 		LOG_ERR("No more timeouts can be installed");
74 		return -ENOMEM;
75 	}
76 
77 	WDOG_GetDefaultConfig(&data->wdog_config);
78 	data->wdog_config.interruptTimeValue = 0U;
79 
80 	if (cfg->window.max < (MSEC_PER_SEC / 2)) {
81 		LOG_ERR("Invalid window max, shortest window is 500ms");
82 		return -EINVAL;
83 	}
84 
85 	data->wdog_config.timeoutValue =
86 		  WDOG_TMOUT_SEC(cfg->window.max);
87 
88 	if (cfg->window.min) {
89 		LOG_ERR("Invalid window.min, Do not support window model");
90 		return -EINVAL;
91 	}
92 	if (data->wdog_config.timeoutValue > 128) {
93 		LOG_ERR("Invalid timeoutValue, valid (0.5s - 128.0s)");
94 		return -EINVAL;
95 	}
96 
97 	data->wdog_config.enableInterrupt = cfg->callback != NULL;
98 	data->callback = cfg->callback;
99 	data->timeout_valid = true;
100 
101 	return 0;
102 }
103 
mcux_wdog_feed(const struct device * dev,int channel_id)104 static int mcux_wdog_feed(const struct device *dev, int channel_id)
105 {
106 	const struct mcux_wdog_config *config = dev->config;
107 	WDOG_Type *base = config->base;
108 
109 	if (channel_id != 0) {
110 		LOG_ERR("Invalid channel id");
111 		return -EINVAL;
112 	}
113 
114 	WDOG_Refresh(base);
115 	LOG_DBG("Fed the watchdog");
116 
117 	return 0;
118 }
119 
mcux_wdog_isr(const struct device * dev)120 static void mcux_wdog_isr(const struct device *dev)
121 {
122 	const struct mcux_wdog_config *config = dev->config;
123 	struct mcux_wdog_data *data = dev->data;
124 	WDOG_Type *base = config->base;
125 	uint32_t flags;
126 
127 	flags = WDOG_GetStatusFlags(base);
128 	WDOG_ClearInterruptStatus(base, flags);
129 
130 	if (data->callback) {
131 		data->callback(dev, 0);
132 	}
133 }
134 
mcux_wdog_init(const struct device * dev)135 static int mcux_wdog_init(const struct device *dev)
136 {
137 	const struct mcux_wdog_config *config = dev->config;
138 
139 	config->irq_config_func(dev);
140 
141 	return 0;
142 }
143 
144 static const struct wdt_driver_api mcux_wdog_api = {
145 	.setup = mcux_wdog_setup,
146 	.disable = mcux_wdog_disable,
147 	.install_timeout = mcux_wdog_install_timeout,
148 	.feed = mcux_wdog_feed,
149 };
150 
151 static void mcux_wdog_config_func(const struct device *dev);
152 
153 static const struct mcux_wdog_config mcux_wdog_config = {
154 	.base = (WDOG_Type *) DT_INST_REG_ADDR(0),
155 	.irq_config_func = mcux_wdog_config_func,
156 };
157 
158 static struct mcux_wdog_data mcux_wdog_data;
159 
160 DEVICE_DT_INST_DEFINE(0,
161 		    &mcux_wdog_init,
162 		    NULL,
163 		    &mcux_wdog_data, &mcux_wdog_config,
164 		    POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
165 		    &mcux_wdog_api);
166 
mcux_wdog_config_func(const struct device * dev)167 static void mcux_wdog_config_func(const struct device *dev)
168 {
169 	IRQ_CONNECT(DT_INST_IRQN(0),
170 		    DT_INST_IRQ(0, priority),
171 		    mcux_wdog_isr, DEVICE_DT_INST_GET(0), 0);
172 
173 	irq_enable(DT_INST_IRQN(0));
174 }
175