weasel_pci.c revision 1.11 1 /* $NetBSD: weasel_pci.c,v 1.11 2008/04/10 19:13:38 cegger Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Herb Peyerl and Jason Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Device driver for the control space on the Middle Digital, Inc.
41 * PCI-Weasel serial console board.
42 *
43 * Since the other functions of the PCI-Weasel already appear in
44 * PCI configuration space, we just need to hook up the watchdog
45 * timer.
46 */
47
48 #include <sys/cdefs.h>
49 __KERNEL_RCSID(0, "$NetBSD: weasel_pci.c,v 1.11 2008/04/10 19:13:38 cegger Exp $");
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/device.h>
54 #include <sys/wdog.h>
55 #include <sys/endian.h>
56
57 #include <sys/bus.h>
58
59 #include <dev/pci/pcireg.h>
60 #include <dev/pci/pcivar.h>
61 #include <dev/pci/pcidevs.h>
62
63 #include <dev/pci/weaselreg.h>
64
65 #include <dev/sysmon/sysmonvar.h>
66
67 struct weasel_softc {
68 struct device sc_dev; /* generic device glue */
69
70 bus_space_tag_t sc_st;
71 bus_space_handle_t sc_sh;
72
73 struct sysmon_wdog sc_smw;
74
75 int sc_wdog_armed;
76 int sc_wdog_period;
77 };
78
79 /* XXX */
80 extern int sysmon_wdog_setmode(struct sysmon_wdog *, int, u_int);
81
82 static int weasel_pci_wdog_setmode(struct sysmon_wdog *);
83 static int weasel_pci_wdog_tickle(struct sysmon_wdog *);
84
85 static int weasel_wait_response(struct weasel_softc *);
86 static int weasel_issue_command(struct weasel_softc *, uint8_t cmd);
87
88 static int weasel_pci_wdog_arm(struct weasel_softc *);
89 static int weasel_pci_wdog_disarm(struct weasel_softc *);
90
91 static int weasel_pci_wdog_query_state(struct weasel_softc *);
92
93 static int
94 weasel_pci_match(struct device *parent, struct cfdata *cf,
95 void *aux)
96 {
97 struct pci_attach_args *pa = aux;
98
99 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_MIDDLE_DIGITAL &&
100 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_MIDDLE_DIGITAL_WEASEL_CONTROL)
101 return (1);
102
103 return (0);
104 }
105
106 static void
107 weasel_pci_attach(struct device *parent, struct device *self,
108 void *aux)
109 {
110 struct weasel_softc *sc = (void *) self;
111 struct pci_attach_args *pa = aux;
112 struct weasel_config_block cfg;
113 const char *vers, *mode;
114 uint8_t v, *cp;
115 uint16_t cfg_size;
116 uint8_t buf[8];
117
118 printf(": PCI-Weasel watchdog timer\n");
119
120 if (pci_mapreg_map(pa, PCI_MAPREG_START,
121 PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0,
122 &sc->sc_st, &sc->sc_sh, NULL, NULL) != 0) {
123 aprint_error_dev(&sc->sc_dev, "unable to map device registers\n");
124 return;
125 }
126
127 /* Ping the Weasel to see if it's alive. */
128 if (weasel_issue_command(sc, OS_CMD_PING)) {
129 aprint_error_dev(&sc->sc_dev, "Weasel didn't respond to PING\n");
130 return;
131 }
132 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
133 if ((v = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD)) !=
134 OS_RET_PONG) {
135 aprint_error_dev(&sc->sc_dev, "unexpected PING response from Weasel: 0x%02x\n", v);
136 return;
137 }
138
139 /* Read the config block. */
140 if (weasel_issue_command(sc, OS_CMD_SHOW_CONFIG)) {
141 aprint_error_dev(&sc->sc_dev, "Weasel didn't respond to SHOW_CONFIG\n");
142 return;
143 }
144 cfg_size = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
145 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
146
147 if (++cfg_size != sizeof(cfg)) {
148 aprint_error_dev(&sc->sc_dev, "weird config block size from Weasel: 0x%03x\n", cfg_size);
149 return;
150 }
151
152 for (cp = (uint8_t *) &cfg; cfg_size != 0; cfg_size--) {
153 if (weasel_wait_response(sc)) {
154 aprint_error_dev(&sc->sc_dev, "Weasel stopped providing config block(%d)\n", cfg_size);
155 return;
156 }
157 *cp++ = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
158 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
159 }
160
161 switch (cfg.cfg_version) {
162 case CFG_VERSION_2:
163 vers="2";
164 switch (cfg.enable_duart_switching) {
165 case 0:
166 mode = "emulation";
167 break;
168 case 1:
169 mode = "serial";
170 break;
171 case 2:
172 mode = "autoswitch";
173 break;
174 default:
175 mode = "unknown";
176 }
177 break;
178
179 default:
180 vers = mode = NULL;
181 }
182
183 if (vers != NULL)
184 printf("%s: %s mode\n", device_xname(&sc->sc_dev),
185 mode);
186 else
187 printf("%s: unknown config version 0x%02x\n", device_xname(&sc->sc_dev),
188 cfg.cfg_version);
189
190 /*
191 * Fetch sw version.
192 */
193 if (weasel_issue_command(sc, OS_CMD_QUERY_SW_VER)) {
194 aprint_error_dev(&sc->sc_dev, "didn't reply to software version query.\n");
195 }
196 else {
197 v = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
198 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
199 if (v>7)
200 printf("%s: weird length for version string(%d).\n",
201 device_xname(&sc->sc_dev), v);
202 bzero(buf, sizeof(buf));
203 for (cp = buf; v != 0; v--) {
204 if (weasel_wait_response(sc)) {
205 printf("%s: Weasel stopped providing version\n",
206 device_xname(&sc->sc_dev));
207 }
208 *cp++ = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
209 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
210 }
211 printf("%s: sw: %s", device_xname(&sc->sc_dev), buf);
212 }
213 /*
214 * Fetch logic version.
215 */
216 if (weasel_issue_command(sc, OS_CMD_QUERY_L_VER)) {
217 aprint_normal("\n");
218 aprint_error_dev(&sc->sc_dev, "didn't reply to logic version query.\n");
219 }
220 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
221 v = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
222 printf(" logic: %03d", v);
223 /*
224 * Fetch vga bios version.
225 */
226 if (weasel_issue_command(sc, OS_CMD_QUERY_VB_VER)) {
227 aprint_normal("\n");
228 aprint_error_dev(&sc->sc_dev, "didn't reply to vga bios version query.\n");
229 }
230 v = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
231 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
232 printf(" vga bios: %d.%d", (v>>4), (v&0x0f));
233 /*
234 * Fetch hw version.
235 */
236 if (weasel_issue_command(sc, OS_CMD_QUERY_HW_VER)) {
237 aprint_normal("\n");
238 aprint_error_dev(&sc->sc_dev, "didn't reply to hardware version query.\n");
239 }
240 v = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
241 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
242 printf(" hw: %d.%d", (v>>4), (v&0x0f));
243
244 printf("\n%s: break passthrough %s", device_xname(&sc->sc_dev),
245 cfg.break_passthru ? "enabled" : "disabled");
246
247 if ((sc->sc_wdog_armed = weasel_pci_wdog_query_state(sc)) == -1)
248 sc->sc_wdog_armed = 0;
249
250 /* Weasel is big-endian */
251 sc->sc_wdog_period = be16toh(cfg.wdt_msec) / 1000;
252
253 printf(", watchdog timer %d sec.\n", sc->sc_wdog_period);
254 sc->sc_smw.smw_name = "weasel";
255 sc->sc_smw.smw_cookie = sc;
256 sc->sc_smw.smw_setmode = weasel_pci_wdog_setmode;
257 sc->sc_smw.smw_tickle = weasel_pci_wdog_tickle;
258 sc->sc_smw.smw_period = sc->sc_wdog_period;
259
260 if (sysmon_wdog_register(&sc->sc_smw) != 0)
261 aprint_error_dev(&sc->sc_dev, "unable to register PC-Weasel watchdog "
262 "with sysmon\n");
263 }
264
265 CFATTACH_DECL(weasel_pci, sizeof(struct weasel_softc),
266 weasel_pci_match, weasel_pci_attach, NULL, NULL);
267
268 static int
269 weasel_wait_response(struct weasel_softc *sc)
270 {
271 int i;
272
273 for (i = 10000; i ; i--) {
274 delay(100);
275 if (bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS) ==
276 OS_WS_HOST_READ)
277 return(0);
278 }
279 return (1);
280 }
281
282 static int
283 weasel_issue_command(struct weasel_softc *sc, uint8_t cmd)
284 {
285 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_WR, cmd);
286 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_HOST_STATUS, OS_HS_WEASEL_READ);
287 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
288 return (weasel_wait_response(sc));
289 }
290
291 static int
292 weasel_pci_wdog_setmode(struct sysmon_wdog *smw)
293 {
294 struct weasel_softc *sc = smw->smw_cookie;
295 int error = 0;
296
297 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
298 error = weasel_pci_wdog_disarm(sc);
299 } else {
300 if (smw->smw_period == WDOG_PERIOD_DEFAULT)
301 smw->smw_period = sc->sc_wdog_period;
302 else if (smw->smw_period != sc->sc_wdog_period) {
303 /* Can't change the period on the Weasel. */
304 return (EINVAL);
305 }
306 error = weasel_pci_wdog_arm(sc);
307 weasel_pci_wdog_tickle(smw);
308 }
309
310 return (error);
311 }
312
313 static int
314 weasel_pci_wdog_tickle(struct sysmon_wdog *smw)
315 {
316 struct weasel_softc *sc = smw->smw_cookie;
317 u_int8_t reg;
318 int x;
319 int s;
320 int error = 0;
321
322 s = splhigh();
323 /*
324 * first we tickle the watchdog
325 */
326 reg = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_CHALLENGE);
327 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_RESPONSE, ~reg);
328
329 /*
330 * then we check to make sure the weasel is still armed. If someone
331 * has rebooted the weasel for whatever reason (firmware update),
332 * then the watchdog timer would no longer be armed and we'd be
333 * servicing nothing. Let the user know that the machine is no
334 * longer being monitored by the weasel.
335 */
336 if((x = weasel_pci_wdog_query_state(sc)) == -1)
337 error = EIO;
338 if (x == 1) {
339 error = 0;
340 } else {
341 printf("%s: Watchdog timer disabled on PC/Weasel! Disarming wdog.\n",
342 device_xname(&sc->sc_dev));
343 sc->sc_wdog_armed = 0;
344 sysmon_wdog_setmode(smw, WDOG_MODE_DISARMED, 0);
345 error = 1;
346 }
347 splx(s);
348
349 return (error);
350 }
351
352 static int
353 weasel_pci_wdog_arm(struct weasel_softc *sc)
354 {
355 u_int8_t reg;
356 int x;
357 int s;
358 int error = 0;
359
360 s = splhigh();
361 if (weasel_issue_command(sc, OS_CMD_WDT_ENABLE)) {
362 printf("%s: no reply to watchdog enable. Check Weasel \"Allow Watchdog\" setting.\n",
363 device_xname(&sc->sc_dev));
364 error = EIO;
365 }
366 reg = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
367 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
368
369 /*
370 * Ensure that the Weasel thinks it's in the same mode we want it to
371 * be in. EIO if not.
372 */
373 x = weasel_pci_wdog_query_state(sc);
374 switch (x) {
375 case -1:
376 error = EIO;
377 break;
378 case 0:
379 sc->sc_wdog_armed = 0;
380 error = EIO;
381 break;
382 case 1:
383 sc->sc_wdog_armed = 1;
384 error = 0;
385 break;
386 }
387
388 splx(s);
389 return(error);
390 }
391
392
393 static int
394 weasel_pci_wdog_disarm(struct weasel_softc *sc)
395 {
396 u_int8_t reg;
397 int x;
398 int s;
399 int error = 0;
400
401 s = splhigh();
402
403 if (weasel_issue_command(sc, OS_CMD_WDT_DISABLE)) {
404 printf("%s: didn't reply to watchdog disable.\n",
405 device_xname(&sc->sc_dev));
406 error = EIO;
407 }
408 reg = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
409 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
410
411 /*
412 * Ensure that the Weasel thinks it's in the same mode we want it to
413 * be in. EIO if not.
414 */
415 x = weasel_pci_wdog_query_state(sc);
416 switch (x) {
417 case -1:
418 error = EIO;
419 break;
420 case 0:
421 sc->sc_wdog_armed = 0;
422 error = 0;
423 break;
424 case 1:
425 sc->sc_wdog_armed = 1;
426 error = EIO;
427 break;
428 }
429
430 splx(s);
431 return(error);
432 }
433
434 static int
435 weasel_pci_wdog_query_state(struct weasel_softc *sc)
436 {
437
438 u_int8_t v;
439
440 if (weasel_issue_command(sc, OS_CMD_WDT_QUERY)) {
441 printf("%s: didn't reply to watchdog state query.\n",
442 device_xname(&sc->sc_dev));
443 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
444 return(-1);
445 }
446 v = bus_space_read_1(sc->sc_st, sc->sc_sh, WEASEL_DATA_RD);
447 bus_space_write_1(sc->sc_st, sc->sc_sh, WEASEL_STATUS, 0);
448 return(v);
449 }
450