1 /* $NetBSD: weasel_pci.c,v 1.13 2008/07/09 14:51:11 joerg Exp $ */
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Herb Peyerl and Jason Thorpe.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Device driver for the control space on the Middle Digital, Inc.
34 * PCI-Weasel serial console board.
36 * Since the other functions of the PCI-Weasel already appear in
37 * PCI configuration space, we just need to hook up the watchdog
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: weasel_pci.c,v 1.13 2008/07/09 14:51:11 joerg Exp $");
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/device.h>
48 #include <sys/endian.h>
52 #include <dev/pci/pcireg.h>
53 #include <dev/pci/pcivar.h>
54 #include <dev/pci/pcidevs.h>
56 #include <dev/pci/weaselreg.h>
58 #include <dev/sysmon/sysmonvar.h>
62 bus_space_tag_t sc_st
;
63 bus_space_handle_t sc_sh
;
65 struct sysmon_wdog sc_smw
;
72 extern int sysmon_wdog_setmode(struct sysmon_wdog
*, int, u_int
);
74 static int weasel_pci_wdog_setmode(struct sysmon_wdog
*);
75 static int weasel_pci_wdog_tickle(struct sysmon_wdog
*);
77 static int weasel_wait_response(struct weasel_softc
*);
78 static int weasel_issue_command(struct weasel_softc
*, uint8_t cmd
);
80 static int weasel_pci_wdog_arm(struct weasel_softc
*);
81 static int weasel_pci_wdog_disarm(struct weasel_softc
*);
83 static int weasel_pci_wdog_query_state(struct weasel_softc
*);
86 weasel_pci_match(device_t parent
, cfdata_t cf
, void *aux
)
88 struct pci_attach_args
*pa
= aux
;
90 if (PCI_VENDOR(pa
->pa_id
) == PCI_VENDOR_MIDDLE_DIGITAL
&&
91 PCI_PRODUCT(pa
->pa_id
) == PCI_PRODUCT_MIDDLE_DIGITAL_WEASEL_CONTROL
)
98 weasel_pci_attach(device_t parent
, device_t self
, void *aux
)
100 struct weasel_softc
*sc
= device_private(self
);
101 struct pci_attach_args
*pa
= aux
;
102 struct weasel_config_block cfg
;
103 const char *vers
, *mode
;
110 printf(": PCI-Weasel watchdog timer\n");
112 if (pci_mapreg_map(pa
, PCI_MAPREG_START
,
113 PCI_MAPREG_TYPE_MEM
|PCI_MAPREG_MEM_TYPE_32BIT
, 0,
114 &sc
->sc_st
, &sc
->sc_sh
, NULL
, NULL
) != 0) {
115 aprint_error_dev(self
, "unable to map device registers\n");
119 /* Ping the Weasel to see if it's alive. */
120 if (weasel_issue_command(sc
, OS_CMD_PING
)) {
121 aprint_error_dev(self
, "Weasel didn't respond to PING\n");
124 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
125 if ((v
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
)) !=
127 aprint_error_dev(self
, "unexpected PING response from Weasel: 0x%02x\n", v
);
131 /* Read the config block. */
132 if (weasel_issue_command(sc
, OS_CMD_SHOW_CONFIG
)) {
133 aprint_error_dev(self
, "Weasel didn't respond to SHOW_CONFIG\n");
136 cfg_size
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
137 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
139 if (++cfg_size
!= sizeof(cfg
)) {
140 aprint_error_dev(self
, "weird config block size from Weasel: 0x%03x\n", cfg_size
);
144 for (cp
= (uint8_t *) &cfg
; cfg_size
!= 0; cfg_size
--) {
145 if (weasel_wait_response(sc
)) {
146 aprint_error_dev(self
, "Weasel stopped providing config block(%d)\n", cfg_size
);
149 *cp
++ = bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
150 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
153 switch (cfg
.cfg_version
) {
156 switch (cfg
.enable_duart_switching
) {
176 printf("%s: %s mode\n", device_xname(self
), mode
);
178 printf("%s: unknown config version 0x%02x\n", device_xname(self
),
184 if (weasel_issue_command(sc
, OS_CMD_QUERY_SW_VER
)) {
185 aprint_error_dev(self
, "didn't reply to software version query.\n");
188 v
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
189 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
191 printf("%s: weird length for version string(%d).\n",
192 device_xname(self
), v
);
193 memset(buf
, 0, sizeof(buf
));
194 for (cp
= buf
; v
!= 0; v
--) {
195 if (weasel_wait_response(sc
)) {
196 printf("%s: Weasel stopped providing version\n",
199 *cp
++ = bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
200 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
202 printf("%s: sw: %s", device_xname(self
), buf
);
205 * Fetch logic version.
207 if (weasel_issue_command(sc
, OS_CMD_QUERY_L_VER
)) {
209 aprint_error_dev(self
, "didn't reply to logic version query.\n");
211 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
212 v
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
213 printf(" logic: %03d", v
);
215 * Fetch vga bios version.
217 if (weasel_issue_command(sc
, OS_CMD_QUERY_VB_VER
)) {
219 aprint_error_dev(self
, "didn't reply to vga bios version query.\n");
221 v
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
222 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
223 printf(" vga bios: %d.%d", (v
>>4), (v
&0x0f));
227 if (weasel_issue_command(sc
, OS_CMD_QUERY_HW_VER
)) {
229 aprint_error_dev(self
, "didn't reply to hardware version query.\n");
231 v
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
232 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
233 printf(" hw: %d.%d", (v
>>4), (v
&0x0f));
235 printf("\n%s: break passthrough %s", device_xname(self
),
236 cfg
.break_passthru
? "enabled" : "disabled");
238 if ((sc
->sc_wdog_armed
= weasel_pci_wdog_query_state(sc
)) == -1)
239 sc
->sc_wdog_armed
= 0;
241 /* Weasel is big-endian */
242 sc
->sc_wdog_period
= be16toh(cfg
.wdt_msec
) / 1000;
244 printf(", watchdog timer %d sec.\n", sc
->sc_wdog_period
);
245 sc
->sc_smw
.smw_name
= "weasel";
246 sc
->sc_smw
.smw_cookie
= sc
;
247 sc
->sc_smw
.smw_setmode
= weasel_pci_wdog_setmode
;
248 sc
->sc_smw
.smw_tickle
= weasel_pci_wdog_tickle
;
249 sc
->sc_smw
.smw_period
= sc
->sc_wdog_period
;
251 if (sysmon_wdog_register(&sc
->sc_smw
) != 0)
252 aprint_error_dev(self
, "unable to register PC-Weasel watchdog "
256 CFATTACH_DECL_NEW(weasel_pci
, sizeof(struct weasel_softc
),
257 weasel_pci_match
, weasel_pci_attach
, NULL
, NULL
);
260 weasel_wait_response(struct weasel_softc
*sc
)
264 for (i
= 10000; i
; i
--) {
266 if (bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
) ==
274 weasel_issue_command(struct weasel_softc
*sc
, uint8_t cmd
)
276 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_WR
, cmd
);
277 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_HOST_STATUS
, OS_HS_WEASEL_READ
);
278 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
279 return (weasel_wait_response(sc
));
283 weasel_pci_wdog_setmode(struct sysmon_wdog
*smw
)
285 struct weasel_softc
*sc
= smw
->smw_cookie
;
288 if ((smw
->smw_mode
& WDOG_MODE_MASK
) == WDOG_MODE_DISARMED
) {
289 error
= weasel_pci_wdog_disarm(sc
);
291 if (smw
->smw_period
== WDOG_PERIOD_DEFAULT
)
292 smw
->smw_period
= sc
->sc_wdog_period
;
293 else if (smw
->smw_period
!= sc
->sc_wdog_period
) {
294 /* Can't change the period on the Weasel. */
297 error
= weasel_pci_wdog_arm(sc
);
298 weasel_pci_wdog_tickle(smw
);
305 weasel_pci_wdog_tickle(struct sysmon_wdog
*smw
)
307 struct weasel_softc
*sc
= smw
->smw_cookie
;
315 * first we tickle the watchdog
317 reg
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_CHALLENGE
);
318 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_RESPONSE
, ~reg
);
321 * then we check to make sure the weasel is still armed. If someone
322 * has rebooted the weasel for whatever reason (firmware update),
323 * then the watchdog timer would no longer be armed and we'd be
324 * servicing nothing. Let the user know that the machine is no
325 * longer being monitored by the weasel.
327 if((x
= weasel_pci_wdog_query_state(sc
)) == -1)
332 printf("%s: Watchdog timer disabled on PC/Weasel! Disarming wdog.\n",
333 device_xname(sc
->sc_dev
));
334 sc
->sc_wdog_armed
= 0;
335 sysmon_wdog_setmode(smw
, WDOG_MODE_DISARMED
, 0);
344 weasel_pci_wdog_arm(struct weasel_softc
*sc
)
352 if (weasel_issue_command(sc
, OS_CMD_WDT_ENABLE
)) {
353 printf("%s: no reply to watchdog enable. Check Weasel \"Allow Watchdog\" setting.\n",
354 device_xname(sc
->sc_dev
));
357 reg
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
358 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
361 * Ensure that the Weasel thinks it's in the same mode we want it to
364 x
= weasel_pci_wdog_query_state(sc
);
370 sc
->sc_wdog_armed
= 0;
374 sc
->sc_wdog_armed
= 1;
385 weasel_pci_wdog_disarm(struct weasel_softc
*sc
)
394 if (weasel_issue_command(sc
, OS_CMD_WDT_DISABLE
)) {
395 printf("%s: didn't reply to watchdog disable.\n",
396 device_xname(sc
->sc_dev
));
399 reg
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
400 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
403 * Ensure that the Weasel thinks it's in the same mode we want it to
406 x
= weasel_pci_wdog_query_state(sc
);
412 sc
->sc_wdog_armed
= 0;
416 sc
->sc_wdog_armed
= 1;
426 weasel_pci_wdog_query_state(struct weasel_softc
*sc
)
431 if (weasel_issue_command(sc
, OS_CMD_WDT_QUERY
)) {
432 printf("%s: didn't reply to watchdog state query.\n",
433 device_xname(sc
->sc_dev
));
434 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);
437 v
= bus_space_read_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_DATA_RD
);
438 bus_space_write_1(sc
->sc_st
, sc
->sc_sh
, WEASEL_STATUS
, 0);