1 /*
2 * Copyright (2) 2019 Vestas Wind Systems A/S
3 *
4 * Based on wdt_mcux_wdog.c, which is:
5 * Copyright (c) 2018, NXP
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #define DT_DRV_COMPAT nxp_kinetis_wdog32
11
12 #include <zephyr/drivers/watchdog.h>
13 #include <zephyr/drivers/clock_control.h>
14 #include <fsl_wdog32.h>
15
16 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
17 #include <zephyr/logging/log.h>
18 #include <zephyr/irq.h>
19 LOG_MODULE_REGISTER(wdt_mcux_wdog32);
20
21 #define MIN_TIMEOUT 1
22
23 struct mcux_wdog32_config {
24 WDOG_Type *base;
25 #if DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency)
26 uint32_t clock_frequency;
27 #else /* !DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency) */
28 const struct device *clock_dev;
29 clock_control_subsys_t clock_subsys;
30 #endif /* !DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency) */
31 wdog32_clock_source_t clk_source;
32 wdog32_clock_prescaler_t clk_divider;
33 void (*irq_config_func)(const struct device *dev);
34 };
35
36 struct mcux_wdog32_data {
37 wdt_callback_t callback;
38 wdog32_config_t wdog_config;
39 bool timeout_valid;
40 };
41
mcux_wdog32_setup(const struct device * dev,uint8_t options)42 static int mcux_wdog32_setup(const struct device *dev, uint8_t options)
43 {
44 const struct mcux_wdog32_config *config = dev->config;
45 struct mcux_wdog32_data *data = dev->data;
46 WDOG_Type *base = config->base;
47
48 if (!data->timeout_valid) {
49 LOG_ERR("No valid timeouts installed");
50 return -EINVAL;
51 }
52
53 data->wdog_config.workMode.enableStop =
54 (options & WDT_OPT_PAUSE_IN_SLEEP) == 0U;
55
56 data->wdog_config.workMode.enableDebug =
57 (options & WDT_OPT_PAUSE_HALTED_BY_DBG) == 0U;
58
59 WDOG32_Init(base, &data->wdog_config);
60 LOG_DBG("Setup the watchdog");
61
62 return 0;
63 }
64
mcux_wdog32_disable(const struct device * dev)65 static int mcux_wdog32_disable(const struct device *dev)
66 {
67 const struct mcux_wdog32_config *config = dev->config;
68 struct mcux_wdog32_data *data = dev->data;
69 WDOG_Type *base = config->base;
70
71 WDOG32_Deinit(base);
72 data->timeout_valid = false;
73 LOG_DBG("Disabled the watchdog");
74
75 return 0;
76 }
77
78 #define MSEC_TO_WDOG32_TICKS(clock_freq, divider, msec) \
79 ((uint32_t)(clock_freq * msec / 1000U / divider))
80
mcux_wdog32_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)81 static int mcux_wdog32_install_timeout(const struct device *dev,
82 const struct wdt_timeout_cfg *cfg)
83 {
84 const struct mcux_wdog32_config *config = dev->config;
85 struct mcux_wdog32_data *data = dev->data;
86 uint32_t clock_freq;
87 int div;
88
89 if (data->timeout_valid) {
90 LOG_ERR("No more timeouts can be installed");
91 return -ENOMEM;
92 }
93
94 #if DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency)
95 clock_freq = config->clock_frequency;
96 #else /* !DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency) */
97 if (!device_is_ready(config->clock_dev)) {
98 LOG_ERR("clock control device not ready");
99 return -ENODEV;
100 }
101
102 if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
103 &clock_freq)) {
104 return -EINVAL;
105 }
106 #endif /* !DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency) */
107
108 div = config->clk_divider == kWDOG32_ClockPrescalerDivide1 ? 1U : 256U;
109
110 WDOG32_GetDefaultConfig(&data->wdog_config);
111
112 data->wdog_config.timeoutValue =
113 MSEC_TO_WDOG32_TICKS(clock_freq, div, cfg->window.max);
114
115 if (cfg->window.min) {
116 data->wdog_config.enableWindowMode = true;
117 data->wdog_config.windowValue =
118 MSEC_TO_WDOG32_TICKS(clock_freq, div, cfg->window.min);
119 } else {
120 data->wdog_config.enableWindowMode = false;
121 data->wdog_config.windowValue = 0;
122 }
123
124 if ((data->wdog_config.timeoutValue < MIN_TIMEOUT) ||
125 (data->wdog_config.timeoutValue < data->wdog_config.windowValue)) {
126 LOG_ERR("Invalid timeout");
127 return -EINVAL;
128 }
129
130 data->wdog_config.prescaler = config->clk_divider;
131 data->wdog_config.clockSource = config->clk_source;
132 data->wdog_config.enableInterrupt = cfg->callback != NULL;
133 data->callback = cfg->callback;
134 data->timeout_valid = true;
135 LOG_DBG("Installed timeout (timeoutValue = %d)",
136 data->wdog_config.timeoutValue);
137
138 return 0;
139 }
140
mcux_wdog32_feed(const struct device * dev,int channel_id)141 static int mcux_wdog32_feed(const struct device *dev, int channel_id)
142 {
143 const struct mcux_wdog32_config *config = dev->config;
144 WDOG_Type *base = config->base;
145
146 if (channel_id != 0) {
147 LOG_ERR("Invalid channel id");
148 return -EINVAL;
149 }
150
151 WDOG32_Refresh(base);
152 LOG_DBG("Fed the watchdog");
153
154 return 0;
155 }
156
mcux_wdog32_isr(const struct device * dev)157 static void mcux_wdog32_isr(const struct device *dev)
158 {
159 const struct mcux_wdog32_config *config = dev->config;
160 struct mcux_wdog32_data *data = dev->data;
161 WDOG_Type *base = config->base;
162 uint32_t flags;
163
164 flags = WDOG32_GetStatusFlags(base);
165 WDOG32_ClearStatusFlags(base, flags);
166
167 if (data->callback) {
168 data->callback(dev, 0);
169 }
170 }
171
mcux_wdog32_init(const struct device * dev)172 static int mcux_wdog32_init(const struct device *dev)
173 {
174 const struct mcux_wdog32_config *config = dev->config;
175
176 config->irq_config_func(dev);
177
178 return 0;
179 }
180
181 static const struct wdt_driver_api mcux_wdog32_api = {
182 .setup = mcux_wdog32_setup,
183 .disable = mcux_wdog32_disable,
184 .install_timeout = mcux_wdog32_install_timeout,
185 .feed = mcux_wdog32_feed,
186 };
187
188 #define TO_WDOG32_CLK_SRC(val) _DO_CONCAT(kWDOG32_ClockSource, val)
189 #define TO_WDOG32_CLK_DIV(val) _DO_CONCAT(kWDOG32_ClockPrescalerDivide, val)
190
191 static void mcux_wdog32_config_func_0(const struct device *dev);
192
193 static const struct mcux_wdog32_config mcux_wdog32_config_0 = {
194 .base = (WDOG_Type *) DT_INST_REG_ADDR(0),
195 #if DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency)
196 .clock_frequency = DT_INST_PROP_BY_PHANDLE(0, clocks, clock_frequency),
197 #else /* !DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency) */
198 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)),
199 .clock_subsys = (clock_control_subsys_t)
200 DT_INST_CLOCKS_CELL(0, name),
201 #endif /* DT_NODE_HAS_PROP(DT_INST_PHANDLE(0, clocks), clock_frequency) */
202 .clk_source =
203 TO_WDOG32_CLK_SRC(DT_INST_PROP(0, clk_source)),
204 .clk_divider =
205 TO_WDOG32_CLK_DIV(DT_INST_PROP(0, clk_divider)),
206 .irq_config_func = mcux_wdog32_config_func_0,
207 };
208
209 static struct mcux_wdog32_data mcux_wdog32_data_0;
210
211 DEVICE_DT_INST_DEFINE(0, &mcux_wdog32_init,
212 NULL, &mcux_wdog32_data_0,
213 &mcux_wdog32_config_0, POST_KERNEL,
214 CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
215 &mcux_wdog32_api);
216
mcux_wdog32_config_func_0(const struct device * dev)217 static void mcux_wdog32_config_func_0(const struct device *dev)
218 {
219 IRQ_CONNECT(DT_INST_IRQN(0),
220 DT_INST_IRQ(0, priority),
221 mcux_wdog32_isr, DEVICE_DT_INST_GET(0), 0);
222
223 irq_enable(DT_INST_IRQN(0));
224 }
225