1 /* $OpenBSD: wdt.c,v 1.27 2024/05/24 06:02:58 jsg Exp $ */
2
3 /*-
4 * Copyright (c) 1998,1999 Alex Nash
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30 #include <sys/param.h>
31 #include <sys/device.h>
32 #include <sys/systm.h>
33
34 #include <machine/bus.h>
35
36 #include <dev/pci/pcivar.h>
37 #include <dev/pci/pcireg.h>
38 #include <dev/pci/pcidevs.h>
39
40 struct wdt_softc {
41 /* wdt_dev must be the first item in the struct */
42 struct device sc_dev;
43
44 /* feature set: 0 = none 1 = temp, buzzer, etc. */
45 int sc_features;
46
47 /* device access through bus space */
48 bus_space_tag_t sc_iot;
49 bus_space_handle_t sc_ioh;
50 };
51
52 int wdt_probe(struct device *, void *, void *);
53 void wdt_attach(struct device *, struct device *, void *);
54 int wdt_activate(struct device *, int);
55
56 int wdt_is501(struct wdt_softc *);
57 void wdt_8254_count(struct wdt_softc *, int, u_int16_t);
58 void wdt_8254_mode(struct wdt_softc *, int, int);
59 int wdt_set_timeout(void *, int);
60 void wdt_init_timer(struct wdt_softc *);
61 void wdt_buzzer_off(struct wdt_softc *);
62 void wdt_timer_disable(struct wdt_softc *);
63 void wdt_buzzer_enable(struct wdt_softc *);
64
65 const struct cfattach wdt_ca = {
66 sizeof(struct wdt_softc), wdt_probe, wdt_attach,
67 NULL, wdt_activate
68 };
69
70 struct cfdriver wdt_cd = {
71 NULL, "wdt", DV_DULL
72 };
73
74 const struct pci_matchid wdt_devices[] = {
75 { PCI_VENDOR_INDCOMPSRC, PCI_PRODUCT_INDCOMPSRC_WDT50X }
76 };
77
78 /*
79 * 8254 counter mappings
80 */
81 #define WDT_8254_TC_LO 0 /* low 16 bits of timeout counter */
82 #define WDT_8254_TC_HI 1 /* high 16 bits of timeout counter */
83 #define WDT_8254_BUZZER 2
84
85 /*
86 * WDT500/501 ports
87 */
88 #define WDT_8254_BASE 0
89 #define WDT_8254_CTL (WDT_8254_BASE + 3)
90 #define WDT_DISABLE_TIMER 7
91 #define WDT_ENABLE_TIMER 7
92
93 /*
94 * WDT501 specific ports
95 */
96 #define WDT_STATUS_REG 4
97 #define WDT_START_BUZZER 4
98 #define WDT_TEMPERATURE 5
99 #define WDT_STOP_BUZZER 5
100
101 int
wdt_probe(struct device * parent,void * match,void * aux)102 wdt_probe(struct device *parent, void *match, void *aux)
103 {
104 return (pci_matchbyid((struct pci_attach_args *)aux, wdt_devices,
105 nitems(wdt_devices)));
106 }
107
108 void
wdt_attach(struct device * parent,struct device * self,void * aux)109 wdt_attach(struct device *parent, struct device *self, void *aux)
110 {
111 struct wdt_softc *wdt = (struct wdt_softc *)self;
112 struct pci_attach_args *pa = (struct pci_attach_args *)aux;
113 bus_size_t iosize;
114
115 /* retrieve the I/O region (BAR2) */
116 if (pci_mapreg_map(pa, 0x18, PCI_MAPREG_TYPE_IO, 0,
117 &wdt->sc_iot, &wdt->sc_ioh, NULL, &iosize, 0) != 0) {
118 printf("%s: couldn't find PCI I/O region\n",
119 wdt->sc_dev.dv_xname);
120 return;
121 }
122
123 /* sanity check I/O size */
124 if (iosize != (bus_size_t)16) {
125 printf("%s: invalid I/O region size\n",
126 wdt->sc_dev.dv_xname);
127 return;
128 }
129
130 /* initialize the watchdog timer structure */
131
132 /* check the feature set available */
133 if (wdt_is501(wdt))
134 wdt->sc_features = 1;
135 else
136 wdt->sc_features = 0;
137
138 if (wdt->sc_features) {
139 /*
140 * turn off the buzzer, it may have been activated
141 * by a previous timeout
142 */
143 wdt_buzzer_off(wdt);
144
145 wdt_buzzer_enable(wdt);
146 }
147
148 /* initialize the timer modes and the lower 16-bit counter */
149 wdt_init_timer(wdt);
150
151 /*
152 * ensure that the watchdog is disabled
153 */
154 wdt_timer_disable(wdt);
155
156 /*
157 * register with the watchdog framework
158 */
159 wdog_register(wdt_set_timeout, wdt);
160 }
161
162 int
wdt_activate(struct device * self,int act)163 wdt_activate(struct device *self, int act)
164 {
165 switch (act) {
166 case DVACT_POWERDOWN:
167 wdog_shutdown(self);
168 break;
169 }
170
171 return (0);
172 }
173
174 /*
175 * wdt_is501
176 *
177 * Returns non-zero if the card is a 501 model.
178 */
179 int
wdt_is501(struct wdt_softc * wdt)180 wdt_is501(struct wdt_softc *wdt)
181 {
182 /*
183 * It makes too much sense to detect the card type
184 * by the device ID, so we have to resort to testing
185 * the presence of a register to determine the type.
186 */
187 int v = bus_space_read_1(wdt->sc_iot, wdt->sc_ioh, WDT_TEMPERATURE);
188
189 /* XXX may not be reliable */
190 if (v == 0 || v == 0xFF)
191 return(0);
192
193 return(1);
194 }
195
196 /*
197 * wdt_8254_count
198 *
199 * Loads the specified counter with the 16-bit value 'v'.
200 */
201 void
wdt_8254_count(struct wdt_softc * wdt,int counter,u_int16_t v)202 wdt_8254_count(struct wdt_softc *wdt, int counter, u_int16_t v)
203 {
204 bus_space_write_1(wdt->sc_iot, wdt->sc_ioh,
205 WDT_8254_BASE + counter, v & 0xFF);
206 bus_space_write_1(wdt->sc_iot, wdt->sc_ioh, WDT_8254_BASE + counter, v >> 8);
207 }
208
209 /*
210 * wdt_8254_mode
211 *
212 * Sets the mode of the specified counter.
213 */
214 void
wdt_8254_mode(struct wdt_softc * wdt,int counter,int mode)215 wdt_8254_mode(struct wdt_softc *wdt, int counter, int mode)
216 {
217 bus_space_write_1(wdt->sc_iot, wdt->sc_ioh, WDT_8254_CTL,
218 (counter << 6) | 0x30 | (mode << 1));
219 }
220
221 /*
222 * wdt_set_timeout
223 *
224 * Load the watchdog timer with the specified number of seconds.
225 * Clamp seconds to be in the interval [2; 1800].
226 */
227 int
wdt_set_timeout(void * self,int seconds)228 wdt_set_timeout(void *self, int seconds)
229 {
230 struct wdt_softc *wdt = (struct wdt_softc *)self;
231
232 u_int16_t v;
233 int s;
234
235 s = splclock();
236
237 wdt_timer_disable(wdt);
238
239 if (seconds == 0) {
240 splx(s);
241 return (0);
242 } else if (seconds < 2)
243 seconds = 2;
244 else if (seconds > 1800)
245 seconds = 1800;
246
247 /* 8254 has been programmed with a 2ms period */
248 v = (u_int16_t)seconds * 50;
249
250 /* load the new timeout count */
251 wdt_8254_count(wdt, WDT_8254_TC_HI, v);
252
253 /* enable the timer */
254 bus_space_write_1(wdt->sc_iot, wdt->sc_ioh, WDT_ENABLE_TIMER, 0);
255
256 splx(s);
257
258 return (seconds);
259 }
260
261 /*
262 * wdt_timer_disable
263 *
264 * Disables the watchdog timer and cancels the scheduled (if any)
265 * kernel timeout.
266 */
267 void
wdt_timer_disable(struct wdt_softc * wdt)268 wdt_timer_disable(struct wdt_softc *wdt)
269 {
270 (void)bus_space_read_1(wdt->sc_iot, wdt->sc_ioh, WDT_DISABLE_TIMER);
271 }
272
273 /*
274 * wdt_init_timer
275 *
276 * Configure the modes for the watchdog counters and initialize
277 * the low 16-bits of the watchdog counter to have a period of
278 * approximately 1/50th of a second.
279 */
280 void
wdt_init_timer(struct wdt_softc * wdt)281 wdt_init_timer(struct wdt_softc *wdt)
282 {
283 wdt_8254_mode(wdt, WDT_8254_TC_LO, 3);
284 wdt_8254_mode(wdt, WDT_8254_TC_HI, 2);
285 wdt_8254_count(wdt, WDT_8254_TC_LO, 41666);
286 }
287
288 /*******************************************************************
289 * WDT501 specific functions
290 *******************************************************************/
291
292 /*
293 * wdt_buzzer_off
294 *
295 * Turns the buzzer off.
296 */
297 void
wdt_buzzer_off(struct wdt_softc * wdt)298 wdt_buzzer_off(struct wdt_softc *wdt)
299 {
300 bus_space_write_1(wdt->sc_iot, wdt->sc_ioh, WDT_STOP_BUZZER, 0);
301 }
302
303 #ifndef WDT_DISABLE_BUZZER
304 /*
305 * wdt_buzzer_enable
306 *
307 * Enables the buzzer when the watchdog counter expires.
308 */
309 void
wdt_buzzer_enable(struct wdt_softc * wdt)310 wdt_buzzer_enable(struct wdt_softc *wdt)
311 {
312 bus_space_write_1(wdt->sc_iot, wdt->sc_ioh, WDT_8254_BUZZER, 1);
313 wdt_8254_mode(wdt, WDT_8254_BUZZER, 1);
314 }
315 #else
316 /*
317 * wdt_buzzer_disable
318 *
319 * Disables the buzzer from sounding when the watchdog counter
320 * expires.
321 */
322 void
wdt_buzzer_disable(struct wdt_softc * wdt)323 wdt_buzzer_disable(struct wdt_softc *wdt)
324 {
325 wdt_8254_mode(wdt, WDT_8254_BUZZER, 0);
326 }
327 #endif
328