1 /* $OpenBSD: wdt.c,v 1.16 2007/08/14 07:16:26 mk 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/types.h>
31 #include <sys/param.h>
32 #include <sys/device.h>
33 #include <sys/kernel.h>
34 #include <sys/proc.h>
35 #include <sys/systm.h>
36
37 #include <machine/bus.h>
38
39 #include <dev/pci/pcivar.h>
40 #include <dev/pci/pcireg.h>
41 #include <dev/pci/pcidevs.h>
42
43 #include <dev/pci/wdt50x.h>
44
45 struct wdt_softc {
46 /* wdt_dev must be the first item in the struct */
47 struct device wdt_dev;
48
49 /* feature set: 0 = none 1 = temp, buzzer, etc. */
50 int features;
51
52 /* device access through bus space */
53 bus_space_tag_t iot;
54 bus_space_handle_t ioh;
55 };
56
57 int wdtprobe(struct device *, void *, void *);
58 void wdtattach(struct device *, struct device *, void *);
59
60 int wdt_is501(struct wdt_softc *);
61 void wdt_8254_count(struct wdt_softc *, int, u_int16_t);
62 void wdt_8254_mode(struct wdt_softc *, int, int);
63 int wdt_set_timeout(void *, int);
64 void wdt_init_timer(struct wdt_softc *);
65 void wdt_buzzer_off(struct wdt_softc *);
66 void wdt_timer_disable(struct wdt_softc *);
67 void wdt_buzzer_enable(struct wdt_softc *);
68
69 struct cfattach wdt_ca = {
70 sizeof(struct wdt_softc), wdtprobe, wdtattach
71 };
72
73 struct cfdriver wdt_cd = {
74 NULL, "wdt", DV_DULL
75 };
76
77 const struct pci_matchid wdt_devices[] = {
78 { PCI_VENDOR_INDCOMPSRC, PCI_PRODUCT_INDCOMPSRC_WDT50x }
79 };
80
81 /*
82 * 8254 counter mappings
83 */
84 #define WDT_8254_TC_LO 0 /* low 16 bits of timeout counter */
85 #define WDT_8254_TC_HI 1 /* high 16 bits of timeout counter */
86 #define WDT_8254_BUZZER 2
87
88 /*
89 * WDT500/501 ports
90 */
91 #define WDT_8254_BASE 0
92 #define WDT_8254_CTL (WDT_8254_BASE + 3)
93 #define WDT_DISABLE_TIMER 7
94 #define WDT_ENABLE_TIMER 7
95
96 /*
97 * WDT501 specific ports
98 */
99 #define WDT_STATUS_REG 4
100 #define WDT_START_BUZZER 4
101 #define WDT_TEMPERATURE 5
102 #define WDT_STOP_BUZZER 5
103
104 int
105 wdtprobe(struct device *parent, void *match, void *aux)
106 {
107 return (pci_matchbyid((struct pci_attach_args *)aux, wdt_devices,
108 sizeof(wdt_devices)/sizeof(wdt_devices[0])));
109 }
110
111 void
112 wdtattach(struct device *parent, struct device *self, void *aux)
113 {
114 struct wdt_softc *wdt = (struct wdt_softc *)self;
115 struct pci_attach_args *const pa = (struct pci_attach_args *)aux;
116 bus_size_t iosize;
117
118 /* retrieve the I/O region (BAR2) */
119 if (pci_mapreg_map(pa, 0x18, PCI_MAPREG_TYPE_IO, 0,
120 &wdt->iot, &wdt->ioh, NULL, &iosize, 0) != 0) {
121 printf("%s: couldn't find PCI I/O region\n",
122 wdt->wdt_dev.dv_xname);
123 return;
124 }
125
126 /* sanity check I/O size */
127 if (iosize != (bus_size_t)16) {
128 printf("%s: invalid I/O region size\n",
129 wdt->wdt_dev.dv_xname);
130 return;
131 }
132
133 /* initialize the watchdog timer structure */
134
135 /* check the feature set available */
136 if (wdt_is501(wdt))
137 wdt->features = 1;
138 else
139 wdt->features = 0;
140
141 if (wdt->features) {
142 /*
143 * turn off the buzzer, it may have been activated
144 * by a previous timeout
145 */
146 wdt_buzzer_off(wdt);
147
148 wdt_buzzer_enable(wdt);
149 }
150
151 /* initialize the timer modes and the lower 16-bit counter */
152 wdt_init_timer(wdt);
153
154 /*
155 * ensure that the watchdog is disabled
156 */
157 wdt_timer_disable(wdt);
158
159 /*
160 * register with the watchdog framework
161 */
162 wdog_register(wdt, wdt_set_timeout);
163 }
164
165 /*
166 * wdt_is501
167 *
168 * Returns non-zero if the card is a 501 model.
169 */
170 int
171 wdt_is501(struct wdt_softc *wdt)
172 {
173 /*
174 * It makes too much sense to detect the card type
175 * by the device ID, so we have to resort to testing
176 * the presence of a register to determine the type.
177 */
178 int v = bus_space_read_1(wdt->iot, wdt->ioh, WDT_TEMPERATURE);
179
180 /* XXX may not be reliable */
181 if (v == 0 || v == 0xFF)
182 return(0);
183
184 return(1);
185 }
186
187 /*
188 * wdt_8254_count
189 *
190 * Loads the specified counter with the 16-bit value 'v'.
191 */
192 void
193 wdt_8254_count(struct wdt_softc *wdt, int counter, u_int16_t v)
194 {
195 bus_space_write_1(wdt->iot, wdt->ioh,
196 WDT_8254_BASE + counter, v & 0xFF);
197 bus_space_write_1(wdt->iot, wdt->ioh, WDT_8254_BASE + counter, v >> 8);
198 }
199
200 /*
201 * wdt_8254_mode
202 *
203 * Sets the mode of the specified counter.
204 */
205 void
206 wdt_8254_mode(struct wdt_softc *wdt, int counter, int mode)
207 {
208 bus_space_write_1(wdt->iot, wdt->ioh, WDT_8254_CTL,
209 (counter << 6) | 0x30 | (mode << 1));
210 }
211
212 /*
213 * wdt_set_timeout
214 *
215 * Load the watchdog timer with the specified number of seconds.
216 * Clamp seconds to be in the interval [2; 1800].
217 */
218 int
219 wdt_set_timeout(void *self, int seconds)
220 {
221 struct wdt_softc *wdt = (struct wdt_softc *)self;
222
223 u_int16_t v;
224 int s;
225
226 s = splclock();
227
228 wdt_timer_disable(wdt);
229
230 if (seconds == 0) {
231 splx(s);
232 return (0);
233 } else if (seconds < 2)
234 seconds = 2;
235 else if (seconds > 1800)
236 seconds = 1800;
237
238 /* 8254 has been programmed with a 2ms period */
239 v = (u_int16_t)seconds * 50;
240
241 /* load the new timeout count */
242 wdt_8254_count(wdt, WDT_8254_TC_HI, v);
243
244 /* enable the timer */
245 bus_space_write_1(wdt->iot, wdt->ioh, WDT_ENABLE_TIMER, 0);
246
247 splx(s);
248
249 return (seconds);
250 }
251
252 /*
253 * wdt_timer_disable
254 *
255 * Disables the watchdog timer and cancels the scheduled (if any)
256 * kernel timeout.
257 */
258 void
259 wdt_timer_disable(struct wdt_softc *wdt)
260 {
261 (void)bus_space_read_1(wdt->iot, wdt->ioh, WDT_DISABLE_TIMER);
262 }
263
264 /*
265 * wdt_init_timer
266 *
267 * Configure the modes for the watchdog counters and initialize
268 * the low 16-bits of the watchdog counter to have a period of
269 * approximately 1/50th of a second.
270 */
271 void
272 wdt_init_timer(struct wdt_softc *wdt)
273 {
274 wdt_8254_mode(wdt, WDT_8254_TC_LO, 3);
275 wdt_8254_mode(wdt, WDT_8254_TC_HI, 2);
276 wdt_8254_count(wdt, WDT_8254_TC_LO, 41666);
277 }
278
279 /*******************************************************************
280 * WDT501 specific functions
281 *******************************************************************/
282
283 /*
284 * wdt_buzzer_off
285 *
286 * Turns the buzzer off.
287 */
288 void
289 wdt_buzzer_off(struct wdt_softc *wdt)
290 {
291 bus_space_write_1(wdt->iot, wdt->ioh, WDT_STOP_BUZZER, 0);
292 }
293
294 #ifndef WDT_DISABLE_BUZZER
295 /*
296 * wdt_buzzer_enable
297 *
298 * Enables the buzzer when the watchdog counter expires.
299 */
300 void
301 wdt_buzzer_enable(struct wdt_softc *wdt)
302 {
303 bus_space_write_1(wdt->iot, wdt->ioh, WDT_8254_BUZZER, 1);
304 wdt_8254_mode(wdt, WDT_8254_BUZZER, 1);
305 }
306 #else
307 /*
308 * wdt_buzzer_disable
309 *
310 * Disables the buzzer from sounding when the watchdog counter
311 * expires.
312 */
313 void
314 wdt_buzzer_disable(struct wdt_softc *wdt)
315 {
316 wdt_8254_mode(wdt, WDT_8254_BUZZER, 0);
317 }
318 #endif