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