1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #define DT_DRV_COMPAT intel_tco_wdt
7 
8 #include <zephyr/kernel.h>
9 #include <zephyr/drivers/watchdog.h>
10 #include <zephyr/logging/log.h>
11 
12 LOG_MODULE_REGISTER(wdt_tco, CONFIG_WDT_LOG_LEVEL);
13 
14 #define BASE(d)                ((struct tco_config *)(d)->config)->base
15 
16 #define TCO_RLD(d)             (BASE(d) + 0x00) /* TCO Timer Reload/Curr. Value */
17 #define TCO_DAT_IN(d)          (BASE(d) + 0x02) /* TCO Data In Register */
18 #define TCO_DAT_OUT(d)         (BASE(d) + 0x03) /* TCO Data Out Register */
19 #define TCO1_STS(d)            (BASE(d) + 0x04) /* TCO1 Status Register */
20 #define TCO2_STS(d)            (BASE(d) + 0x06) /* TCO2 Status Register */
21 #define TCO1_CNT(d)            (BASE(d) + 0x08) /* TCO1 Control Register */
22 #define TCO2_CNT(d)            (BASE(d) + 0x0a) /* TCO2 Control Register */
23 #define TCO_MSG(d)             (BASE(d) + 0x0c) /* TCO Message Registers */
24 #define TCO_WDSTATUS(d)        (BASE(d) + 0x0e) /* TCO Watchdog Status Register */
25 #define TCO_TMR(d)             (BASE(d) + 0x12) /* TCO Timer Register */
26 
27 /* TCO1_STS bits */
28 #define STS_NMI2SMI            BIT(0)
29 #define STS_OS_TCO_SMI         BIT(1)
30 #define STS_TCO_INT            BIT(2)
31 #define STS_TIMEOUT            BIT(3)
32 #define STS_NEWCENTURY         BIT(7)
33 #define STS_BIOSWR             BIT(8)
34 #define STS_CPUSCI             BIT(9)
35 #define STS_CPUSMI             BIT(10)
36 #define STS_CPUSERR            BIT(12)
37 #define STS_SLVSEL             BIT(13)
38 
39 /* TCO2_STS bits */
40 #define STS_INTRD_DET          BIT(0)
41 #define STS_SECOND_TO          BIT(1)
42 #define STS_NRSTRAP            BIT(2)
43 #define STS_SMLINK_SLAVE_SMI   BIT(3)
44 
45 /* TCO1_CNT bits */
46 #define CNT_NR_MSUS            BIT(0)
47 #define CNT_NMI_NOW            BIT(8)
48 #define CNT_NMI2SMI_EN         BIT(9)
49 #define CNT_TCO_TMR_HALT       BIT(11)
50 #define CNT_TCO_LOCK           BIT(12)
51 
52 /* TCO_TMR bits */
53 #define TMR_TCOTMR             BIT_MASK(10)
54 #define TMR_MIN                0x04
55 #define TMR_MAX                0x3f
56 
57 struct tco_data {
58 	struct k_spinlock lock;
59 	bool no_reboot;
60 };
61 
62 struct tco_config {
63 	io_port_t base;
64 };
65 
set_no_reboot(const struct device * dev,bool set)66 static int set_no_reboot(const struct device *dev, bool set)
67 {
68 	uint16_t val, newval;
69 
70 	val = sys_in16(TCO1_CNT(dev));
71 
72 	if (set) {
73 		val |= CNT_NR_MSUS;
74 	} else {
75 		val &= ~CNT_NR_MSUS;
76 	}
77 
78 	sys_out16(val, TCO1_CNT(dev));
79 	newval = sys_in16(TCO1_CNT(dev));
80 
81 	if (val != newval) {
82 		return -EIO;
83 	}
84 
85 	return 0;
86 }
87 
tco_setup(const struct device * dev,uint8_t options)88 static int tco_setup(const struct device *dev, uint8_t options)
89 {
90 	struct tco_data *data = dev->data;
91 	k_spinlock_key_t key;
92 	uint16_t val;
93 	int err;
94 
95 	key = k_spin_lock(&data->lock);
96 
97 	err = set_no_reboot(dev, data->no_reboot);
98 	if (err) {
99 		k_spin_unlock(&data->lock, key);
100 		LOG_ERR("Failed to update no_reboot bit (err %d)", err);
101 		return err;
102 	}
103 
104 	/* Reload the timer */
105 	sys_out16(0x01, TCO_RLD(dev));
106 
107 	/* Enable the timer to start counting by clearing the TCO_TMR_HALT field */
108 	val = sys_in16(TCO1_CNT(dev));
109 	val &= ~CNT_TCO_TMR_HALT;
110 	sys_out16(val, TCO1_CNT(dev));
111 	val = sys_in16(TCO1_CNT(dev));
112 
113 	k_spin_unlock(&data->lock, key);
114 
115 	if ((val & CNT_TCO_TMR_HALT) == CNT_TCO_TMR_HALT) {
116 		return -EIO;
117 	}
118 
119 	return 0;
120 }
121 
tco_disable(const struct device * dev)122 static int tco_disable(const struct device *dev)
123 {
124 	struct tco_data *data = dev->data;
125 	k_spinlock_key_t key;
126 	uint16_t val;
127 
128 	key = k_spin_lock(&data->lock);
129 
130 	/* Set the TCO_TMR_HALT field so that the timer gets halted */
131 	val = sys_in16(TCO1_CNT(dev));
132 	val |= CNT_TCO_TMR_HALT;
133 	sys_out16(val, TCO1_CNT(dev));
134 	val = sys_in16(TCO1_CNT(dev));
135 
136 	set_no_reboot(dev, true);
137 
138 	k_spin_unlock(&data->lock, key);
139 
140 	if ((val & CNT_TCO_TMR_HALT) == 0) {
141 		return -EIO;
142 	}
143 
144 	return 0;
145 }
146 
msec_to_ticks(uint32_t msec)147 static uint16_t msec_to_ticks(uint32_t msec)
148 {
149 	/* Convert from milliseconds to timer ticks. The timer is clocked at
150 	 * approximately 0.6 seconds.
151 	 */
152 	return ((msec / MSEC_PER_SEC) * 10) / 6;
153 }
154 
tco_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)155 static int tco_install_timeout(const struct device *dev,
156 			       const struct wdt_timeout_cfg *cfg)
157 {
158 	struct tco_data *data = dev->data;
159 	k_spinlock_key_t key;
160 	uint16_t val, ticks;
161 
162 	/* TCO watchdog doesn't support windowed timeouts */
163 	if (cfg->window.min != 0) {
164 		return -EINVAL;
165 	}
166 
167 	/* No callback support */
168 	if (cfg->callback != NULL) {
169 		return -ENOTSUP;
170 	}
171 
172 	ticks = msec_to_ticks(cfg->window.max);
173 
174 	LOG_DBG("window.max %u -> ticks %u", cfg->window.max, ticks);
175 
176 	if (ticks < TMR_MIN || ticks > TMR_MAX) {
177 		return -EINVAL;
178 	}
179 
180 	switch (cfg->flags) {
181 	case WDT_FLAG_RESET_SOC:
182 		data->no_reboot = false;
183 		break;
184 	case WDT_FLAG_RESET_NONE:
185 		data->no_reboot = true;
186 		break;
187 	case WDT_FLAG_RESET_CPU_CORE:
188 		LOG_ERR("CPU-only reset not supported");
189 		return -ENOTSUP;
190 	default:
191 		LOG_ERR("Unknown watchdog configuration flags");
192 		return -EINVAL;
193 	}
194 
195 	key = k_spin_lock(&data->lock);
196 
197 	/* Set the TCO_TMR field. This value is loaded into the timer each time
198 	 * the TCO_RLD register is written.
199 	 */
200 	val = sys_in16(TCO_TMR(dev));
201 	val &= ~TMR_TCOTMR;
202 	val |= ticks;
203 	sys_out16(val, TCO_TMR(dev));
204 	val = sys_in16(TCO_TMR(dev));
205 
206 	k_spin_unlock(&data->lock, key);
207 
208 	if ((val & TMR_TCOTMR) != ticks) {
209 		LOG_ERR("val %u", val);
210 		return -EIO;
211 	}
212 
213 	return 0;
214 }
215 
tco_feed(const struct device * dev,int channel_id)216 static int tco_feed(const struct device *dev, int channel_id)
217 {
218 	struct tco_data *data = dev->data;
219 	k_spinlock_key_t key;
220 
221 	key = k_spin_lock(&data->lock);
222 
223 	/* TCORLD: Writing any value to this register will reload the timer to
224 	 * prevent the timeout.
225 	 */
226 	sys_out16(0x01, TCO_RLD(dev));
227 
228 	k_spin_unlock(&data->lock, key);
229 
230 	return 0;
231 }
232 
233 static DEVICE_API(wdt, tco_driver_api) = {
234 	.setup = tco_setup,
235 	.disable = tco_disable,
236 	.install_timeout = tco_install_timeout,
237 	.feed = tco_feed,
238 };
239 
wdt_init(const struct device * dev)240 static int wdt_init(const struct device *dev)
241 {
242 	const struct tco_config *config = dev->config;
243 	struct tco_data *data = dev->data;
244 	k_spinlock_key_t key;
245 
246 	LOG_DBG("Using 0x%04x as TCOBA", config->base);
247 
248 	key = k_spin_lock(&data->lock);
249 
250 	sys_out16(STS_TIMEOUT, TCO1_STS(dev)); /* Clear the Time Out Status bit */
251 	sys_out16(STS_SECOND_TO, TCO2_STS(dev)); /* Clear SECOND_TO_STS bit */
252 
253 	set_no_reboot(dev, data->no_reboot);
254 
255 	k_spin_unlock(&data->lock, key);
256 
257 	if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) {
258 		tco_disable(dev);
259 	}
260 
261 	return 0;
262 }
263 
264 static struct tco_data wdt_data = {
265 };
266 
267 static const struct tco_config wdt_config = {
268 	.base = DT_INST_REG_ADDR(0),
269 };
270 
271 DEVICE_DT_INST_DEFINE(0, wdt_init, NULL, &wdt_data, &wdt_config,
272 		      POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
273 		      &tco_driver_api);
274