1 /*
2  * Copyright (c) 2021 ITE Corporation. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT ite_it8xxx2_watchdog
7 
8 #include <zephyr/drivers/watchdog.h>
9 #include <zephyr/irq.h>
10 #include <errno.h>
11 #include <soc.h>
12 
13 #include <zephyr/logging/log.h>
14 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
15 LOG_MODULE_REGISTER(wdt_ite_it8xxx2);
16 
17 #define IT8XXX2_WATCHDOG_MAGIC_BYTE			0x5c
18 #define WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(ms)	((ms) * 1024 / 1000)
19 
20 /* enter critical period or not */
21 static int wdt_warning_fired;
22 
23 /* device config */
24 struct wdt_it8xxx2_config {
25 	/* wdt register base address */
26 	struct wdt_it8xxx2_regs *base;
27 };
28 
29 /* driver data */
30 struct wdt_it8xxx2_data {
31 	/* timeout callback used to handle watchdog event */
32 	wdt_callback_t callback;
33 	/* indicate whether a watchdog timeout is installed */
34 	bool timeout_installed;
35 	/* watchdog feed timeout in milliseconds */
36 	uint32_t timeout;
37 };
38 
wdt_it8xxx2_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * config)39 static int wdt_it8xxx2_install_timeout(const struct device *dev,
40 					  const struct wdt_timeout_cfg *config)
41 {
42 	const struct wdt_it8xxx2_config *const wdt_config = dev->config;
43 	struct wdt_it8xxx2_data *data = dev->data;
44 	struct wdt_it8xxx2_regs *const inst = wdt_config->base;
45 
46 	/* if watchdog is already running */
47 	if ((inst->ETWCFG) & IT8XXX2_WDT_LEWDCNTL) {
48 		return -EBUSY;
49 	}
50 
51 	/*
52 	 * Not support lower limit window timeouts (min value must be equal to
53 	 * 0). Upper limit window timeouts can't be 0 when we install timeout.
54 	 */
55 	if ((config->window.min != 0) || (config->window.max == 0)) {
56 		data->timeout_installed = false;
57 		return -EINVAL;
58 	}
59 
60 	/* save watchdog timeout */
61 	data->timeout = config->window.max;
62 
63 	/* install user timeout isr */
64 	data->callback = config->callback;
65 
66 	/* mark installed */
67 	data->timeout_installed = true;
68 
69 	return 0;
70 }
71 
wdt_it8xxx2_setup(const struct device * dev,uint8_t options)72 static int wdt_it8xxx2_setup(const struct device *dev, uint8_t options)
73 {
74 	const struct wdt_it8xxx2_config *const wdt_config = dev->config;
75 	struct wdt_it8xxx2_data *data = dev->data;
76 	struct wdt_it8xxx2_regs *const inst = wdt_config->base;
77 	uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(data->timeout);
78 	uint16_t cnt1 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT((data->timeout
79 			+ CONFIG_WDT_ITE_WARNING_LEADING_TIME_MS));
80 
81 	/* disable pre-warning timer1 interrupt */
82 	irq_disable(DT_INST_IRQN(0));
83 
84 	if (!data->timeout_installed) {
85 		LOG_ERR("No valid WDT timeout installed");
86 		return -EINVAL;
87 	}
88 
89 	if ((inst->ETWCFG) & IT8XXX2_WDT_LEWDCNTL) {
90 		LOG_ERR("WDT is already running");
91 		return -EBUSY;
92 	}
93 
94 	if ((options & WDT_OPT_PAUSE_IN_SLEEP) != 0) {
95 		LOG_ERR("WDT_OPT_PAUSE_IN_SLEEP is not supported");
96 		return -ENOTSUP;
97 	}
98 
99 	/* pre-warning timer1 is 16-bit counter down timer */
100 	inst->ET1CNTLHR = (cnt0 >> 8) & 0xff;
101 	inst->ET1CNTLLR = cnt0 & 0xff;
102 
103 	/* clear pre-warning timer1 interrupt status */
104 	ite_intc_isr_clear(DT_INST_IRQN(0));
105 
106 	/* enable pre-warning timer1 interrupt */
107 	irq_enable(DT_INST_IRQN(0));
108 
109 	/* don't stop watchdog timer counting */
110 	inst->ETWCTRL &= ~IT8XXX2_WDT_EWDSCEN;
111 
112 	/* set watchdog timer count */
113 	inst->EWDCNTHR = (cnt1 >> 8) & 0xff;
114 	inst->EWDCNTLR = cnt1 & 0xff;
115 
116 	/* allow to write timer1 count register */
117 	inst->ETWCFG &= ~IT8XXX2_WDT_LET1CNTL;
118 
119 	/*
120 	 * bit5 = 1: enable key match function to touch watchdog
121 	 * bit4 = 1: select watchdog clock source from prescaler
122 	 * bit3 = 1: lock watchdog count register (also mark as watchdog running)
123 	 * bit1 = 1: lock timer1 prescaler register
124 	 */
125 	inst->ETWCFG = (IT8XXX2_WDT_EWDKEYEN |
126 			IT8XXX2_WDT_EWDSRC |
127 			IT8XXX2_WDT_LEWDCNTL |
128 			IT8XXX2_WDT_LET1PS);
129 
130 	LOG_DBG("WDT Setup and enabled");
131 
132 	return 0;
133 }
134 
135 /*
136  * reload the WDT and pre-warning timer1 counter
137  *
138  * @param dev Pointer to the device structure for the driver instance.
139  * @param channel_id Index of the fed channel, and we only support
140  *                   channel_id = 0 now.
141  */
wdt_it8xxx2_feed(const struct device * dev,int channel_id)142 static int wdt_it8xxx2_feed(const struct device *dev, int channel_id)
143 {
144 	const struct wdt_it8xxx2_config *const wdt_config = dev->config;
145 	struct wdt_it8xxx2_data *data = dev->data;
146 	struct wdt_it8xxx2_regs *const inst = wdt_config->base;
147 	uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(data->timeout);
148 
149 	ARG_UNUSED(channel_id);
150 
151 	/* reset pre-warning timer1 */
152 	inst->ETWCTRL |= IT8XXX2_WDT_ET1RST;
153 
154 	/* restart watchdog timer */
155 	inst->EWDKEYR = IT8XXX2_WATCHDOG_MAGIC_BYTE;
156 
157 	/* reset pre-warning timer1 to default if time is touched */
158 	if (wdt_warning_fired) {
159 		wdt_warning_fired = 0;
160 
161 		/* pre-warning timer1 is 16-bit counter down timer */
162 		inst->ET1CNTLHR = (cnt0 >> 8) & 0xff;
163 		inst->ET1CNTLLR = cnt0 & 0xff;
164 
165 		/* clear timer1 interrupt status */
166 		ite_intc_isr_clear(DT_INST_IRQN(0));
167 
168 		/* enable timer1 interrupt */
169 		irq_enable(DT_INST_IRQN(0));
170 	}
171 
172 	LOG_DBG("WDT Kicking");
173 
174 	return 0;
175 }
176 
wdt_it8xxx2_disable(const struct device * dev)177 static int wdt_it8xxx2_disable(const struct device *dev)
178 {
179 	const struct wdt_it8xxx2_config *const wdt_config = dev->config;
180 	struct wdt_it8xxx2_data *data = dev->data;
181 	struct wdt_it8xxx2_regs *const inst = wdt_config->base;
182 
183 	/* stop watchdog timer counting */
184 	inst->ETWCTRL |= IT8XXX2_WDT_EWDSCEN;
185 
186 	/* unlock watchdog count register (also mark as watchdog not running) */
187 	inst->ETWCFG &= ~IT8XXX2_WDT_LEWDCNTL;
188 
189 	/* disable pre-warning timer1 interrupt */
190 	irq_disable(DT_INST_IRQN(0));
191 
192 	/* mark uninstalled */
193 	data->timeout_installed = false;
194 
195 	LOG_DBG("WDT Disabled");
196 
197 	return 0;
198 }
199 
wdt_it8xxx2_isr(const struct device * dev)200 static void wdt_it8xxx2_isr(const struct device *dev)
201 {
202 	const struct wdt_it8xxx2_config *const wdt_config = dev->config;
203 	struct wdt_it8xxx2_data *data = dev->data;
204 	struct wdt_it8xxx2_regs *const inst = wdt_config->base;
205 
206 	/* clear pre-warning timer1 interrupt status */
207 	ite_intc_isr_clear(DT_INST_IRQN(0));
208 
209 	/* reset pre-warning timer1 */
210 	inst->ETWCTRL |= IT8XXX2_WDT_ET1RST;
211 
212 	/* callback function, ex. print warning message */
213 	if (data->callback) {
214 		data->callback(dev, 0);
215 	}
216 
217 	if (IS_ENABLED(CONFIG_WDT_ITE_REDUCE_WARNING_LEADING_TIME)) {
218 		/*
219 		 * Once warning timer triggered: if watchdog timer isn't reloaded,
220 		 * then we will reduce interval of warning timer to 30ms to print
221 		 * more warning messages before watchdog reset.
222 		 */
223 		if (!wdt_warning_fired) {
224 			uint16_t cnt0 = WARNING_TIMER_PERIOD_MS_TO_1024HZ_COUNT(30);
225 
226 			/* pre-warning timer1 is 16-bit counter down timer */
227 			inst->ET1CNTLHR = (cnt0 >> 8) & 0xff;
228 			inst->ET1CNTLLR = cnt0 & 0xff;
229 
230 			/* clear pre-warning timer1 interrupt status */
231 			ite_intc_isr_clear(DT_INST_IRQN(0));
232 		}
233 	}
234 	wdt_warning_fired++;
235 
236 	LOG_DBG("WDT ISR");
237 }
238 
239 static const struct wdt_driver_api wdt_it8xxx2_api = {
240 	.setup = wdt_it8xxx2_setup,
241 	.disable = wdt_it8xxx2_disable,
242 	.install_timeout = wdt_it8xxx2_install_timeout,
243 	.feed = wdt_it8xxx2_feed,
244 };
245 
wdt_it8xxx2_init(const struct device * dev)246 static int wdt_it8xxx2_init(const struct device *dev)
247 {
248 	const struct wdt_it8xxx2_config *const wdt_config = dev->config;
249 	struct wdt_it8xxx2_regs *const inst = wdt_config->base;
250 
251 	if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) {
252 		wdt_it8xxx2_disable(dev);
253 	}
254 
255 	/* unlock access to watchdog registers */
256 	inst->ETWCFG = 0x00;
257 
258 	/* set WDT and timer1 to use 1.024kHz clock */
259 	inst->ET1PSR = IT8XXX2_WDT_ETPS_1P024_KHZ;
260 
261 	/* set WDT key match enabled and WDT clock to use ET1PSR */
262 	inst->ETWCFG = (IT8XXX2_WDT_EWDKEYEN |
263 			IT8XXX2_WDT_EWDSRC);
264 
265 	/*
266 	 * select the mode that watchdog can be stopped, this is needed for
267 	 * wdt_it8xxx2_disable() api and WDT_OPT_PAUSE_HALTED_BY_DBG flag
268 	 */
269 	inst->ETWCTRL |= IT8XXX2_WDT_EWDSCMS;
270 
271 	IRQ_CONNECT(DT_INST_IRQN(0), 0, wdt_it8xxx2_isr,
272 		    DEVICE_DT_INST_GET(0), 0);
273 	return 0;
274 }
275 
276 static const struct wdt_it8xxx2_config wdt_it8xxx2_cfg_0 = {
277 	.base = (struct wdt_it8xxx2_regs *)DT_INST_REG_ADDR(0),
278 };
279 
280 static struct wdt_it8xxx2_data wdt_it8xxx2_dev_data;
281 
282 DEVICE_DT_INST_DEFINE(0, wdt_it8xxx2_init, NULL,
283 			&wdt_it8xxx2_dev_data, &wdt_it8xxx2_cfg_0,
284 			PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
285 			&wdt_it8xxx2_api);
286