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