1 /* $NetBSD: weasel_isa.c,v 1.1 2001/12/16 22:35:31 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 Zembu Labs, Inc. 5 * All rights reserved. 6 * 7 * Author: Jason R. Thorpe <thorpej@zembu.com> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Zembu Labs, Inc. 20 * 4. Neither the name of Zembu Labs nor the names of its employees may 21 * be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS 25 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- 26 * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- 27 * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /* 37 * Device driver for the Middle Digital, Inc. PC-Weasel serial 38 * console board. 39 * 40 * We're glued into the MDA display driver (`pcdisplay'), and 41 * handle things like the watchdog timer. 42 */ 43 44 #include <sys/cdefs.h> 45 __KERNEL_RCSID(0, "$NetBSD: weasel_isa.c,v 1.1 2001/12/16 22:35:31 thorpej Exp $"); 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/device.h> 50 #include <sys/wdog.h> 51 52 #include <machine/bus.h> 53 54 #include <dev/isa/weaselreg.h> 55 #include <dev/isa/weaselvar.h> 56 57 #include <dev/sysmon/sysmonvar.h> 58 59 int weasel_isa_wdog_setmode(struct sysmon_wdog *); 60 int weasel_isa_wdog_tickle(struct sysmon_wdog *); 61 int weasel_isa_wdog_arm_disarm(struct weasel_handle *, u_int8_t); 62 int weasel_isa_wdog_query_state(struct weasel_handle *); 63 64 65 void pcweaselattach(int); 66 67 /* ARGSUSED */ 68 void 69 pcweaselattach(int count) 70 { 71 72 /* Nothing to do; pseudo-device glue. */ 73 } 74 75 void 76 weasel_isa_init(struct weasel_handle *wh) 77 { 78 struct weasel_config_block cfg; 79 int i, j; 80 u_int8_t *cp, sum; 81 const char *vers, *mode; 82 83 /* 84 * Write a NOP to the command register and see if it 85 * reverts back to READY within 1.5 seconds. 86 */ 87 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_MISC_COMMAND, OS_NOP); 88 for (i = 0; i < 1500; i++) { 89 delay(1000); 90 sum = bus_space_read_1(wh->wh_st, wh->wh_sh, 91 WEASEL_MISC_COMMAND); 92 if (sum == OS_READY) 93 break; 94 } 95 if (sum != OS_READY) { 96 /* This is not a Weasel. */ 97 return; 98 } 99 100 /* 101 * It can take a while for the config block to be copied 102 * into the offscreen area, as the Weasel may be busy 103 * sending data to the terminal. Wait up to 3 seconds, 104 * reading the block each time, and breaking out of the 105 * loop once the checksum passes. 106 */ 107 108 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_MISC_COMMAND, 109 OS_CONFIG_COPY); 110 111 /* ...one second to get it started... */ 112 delay(1000 * 1000); 113 114 /* ...two seconds to let it finish... */ 115 for (i = 0; i < 2000; i++) { 116 delay(1000); 117 bus_space_read_region_1(wh->wh_st, wh->wh_sh, 118 WEASEL_CONFIG_BLOCK, &cfg, sizeof(cfg)); 119 /* 120 * Compute the checksum of the config block. 121 */ 122 for (cp = (u_int8_t *)&cfg, j = 0, sum = 1; 123 j < (sizeof(cfg) - 1); j++) 124 sum += cp[j]; 125 if (sum == cfg.cksum) 126 break; 127 } 128 129 if (sum != cfg.cksum) { 130 /* 131 * Checksum doesn't match; either it's not a Weasel, 132 * or something is wrong with it. 133 */ 134 printf("%s: PC-Weasel config block checksum mismatch " 135 "0x%02x != 0x%02x\n", wh->wh_parent->dv_xname, 136 sum, cfg.cksum); 137 return; 138 } 139 140 switch (cfg.cfg_version) { 141 case CFG_VERSION_1_0: 142 vers = "1.0"; 143 switch (cfg.enable_duart_switching) { 144 case 0: 145 mode = "emulation"; 146 break; 147 148 case 1: 149 mode = "autoswitch"; 150 break; 151 152 default: 153 mode = "unknown"; 154 } 155 break; 156 157 case CFG_VERSION_1_1: 158 vers = "1.1"; 159 switch (cfg.enable_duart_switching) { 160 case 0: 161 mode = "emulation"; 162 break; 163 164 case 1: 165 mode = "serial"; 166 break; 167 168 case 2: 169 mode = "autoswitch"; 170 break; 171 172 default: 173 mode = "unknown"; 174 } 175 break; 176 177 default: 178 vers = mode = NULL; 179 } 180 181 printf("%s: PC-Weasel, ", wh->wh_parent->dv_xname); 182 if (vers != NULL) 183 printf("version %s, %s mode", vers, mode); 184 else 185 printf("unknown version 0x%x", cfg.cfg_version); 186 printf("\n"); 187 188 printf("%s: break passthrough %s", wh->wh_parent->dv_xname, 189 cfg.break_passthru ? "enabled" : "disabled"); 190 if (cfg.wdt_msec == 0) { 191 /* 192 * Old firmware -- these Weasels have 193 * a 3000ms watchdog period. 194 */ 195 cfg.wdt_msec = 3000; 196 } 197 198 if ((wh->wh_wdog_armed = weasel_isa_wdog_query_state(wh)) == -1) 199 wh->wh_wdog_armed = 0; 200 wh->wh_wdog_period = cfg.wdt_msec / 1000; 201 202 printf(", watchdog interval %d sec.\n", wh->wh_wdog_period); 203 204 /* 205 * Always register the Weasel watchdog timer in case user decides 206 * to set 'allow watchdog' to 'YES' after the machine has booted. 207 */ 208 wh->wh_smw.smw_name = "weasel"; 209 wh->wh_smw.smw_cookie = wh; 210 wh->wh_smw.smw_setmode = weasel_isa_wdog_setmode; 211 wh->wh_smw.smw_tickle = weasel_isa_wdog_tickle; 212 wh->wh_smw.smw_period = wh->wh_wdog_period; 213 214 if (sysmon_wdog_register(&wh->wh_smw) != 0) 215 printf("%s: unable to register PC-Weasel watchdog " 216 "with sysmon\n", wh->wh_parent->dv_xname); 217 } 218 219 int 220 weasel_isa_wdog_setmode(struct sysmon_wdog *smw) 221 { 222 struct weasel_handle *wh = smw->smw_cookie; 223 int error = 0; 224 225 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 226 error = weasel_isa_wdog_arm_disarm(wh, WDT_DISABLE); 227 } else { 228 if (smw->smw_period == WDOG_PERIOD_DEFAULT) 229 smw->smw_period = wh->wh_wdog_period; 230 else if (smw->smw_period != wh->wh_wdog_period) { 231 /* Can't change the period on the Weasel. */ 232 return (EINVAL); 233 } 234 error = weasel_isa_wdog_arm_disarm(wh, WDT_ENABLE); 235 weasel_isa_wdog_tickle(smw); 236 } 237 238 return (error); 239 } 240 241 int 242 weasel_isa_wdog_tickle(struct sysmon_wdog *smw) 243 { 244 struct weasel_handle *wh = smw->smw_cookie; 245 u_int8_t reg; 246 int x; 247 int s; 248 int error = 0; 249 250 s = splhigh(); 251 /* 252 * first we tickle the watchdog 253 */ 254 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_TICKLE); 255 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_TICKLE, ~reg); 256 257 /* 258 * then we check to make sure the weasel is still armed. If someone 259 * has rebooted the weasel for whatever reason (firmware update), 260 * then the watchdog timer would no longer be armed and we'd be 261 * servicing nothing. Let the user know that the machine is no 262 * longer being monitored by the weasel. 263 */ 264 if((x = weasel_isa_wdog_query_state(wh)) == -1) 265 error = EIO; 266 if (x == 1) { 267 error = 0; 268 } else { 269 printf("%s: Watchdog timer disabled on PC/Weasel! Disarming wdog.\n", 270 wh->wh_parent->dv_xname); 271 wh->wh_wdog_armed = 0; 272 error = 1; 273 } 274 splx(s); 275 276 return (error); 277 } 278 279 int 280 weasel_isa_wdog_arm_disarm(struct weasel_handle *wh, u_int8_t mode) 281 { 282 u_int8_t reg; 283 int timeout; 284 int s, x; 285 int error = 0; 286 287 s = splhigh(); 288 289 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_SEMAPHORE, 290 WDT_ATTENTION); 291 for (timeout = 5000; timeout; timeout--) { 292 delay(1500); 293 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 294 WEASEL_WDT_SEMAPHORE); 295 if (reg == WDT_OK) 296 break; 297 } 298 if (timeout == 0) { 299 splx(s); 300 return(EIO); 301 } 302 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_SEMAPHORE, mode); 303 for (timeout = 500 ; timeout; timeout--) { 304 delay(1500); 305 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 306 WEASEL_WDT_SEMAPHORE); 307 if (reg != mode) 308 break; 309 } 310 if (timeout == 0) { 311 splx(s); 312 return(EIO); 313 } 314 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_SEMAPHORE, ~reg); 315 for (timeout = 500; timeout; timeout--) { 316 delay(1500); 317 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 318 WEASEL_WDT_SEMAPHORE); 319 if (reg == WDT_OK) 320 break; 321 } 322 323 /* 324 * Ensure that the Weasel thinks it's in the same mode we want it to 325 * be in. EIO if not. 326 */ 327 x = weasel_isa_wdog_query_state(wh); 328 switch (x) { 329 case -1: 330 error = EIO; 331 break; 332 case 0: 333 if (mode == WDT_DISABLE) { 334 wh->wh_wdog_armed = 0; 335 error = 0; 336 } else 337 error = EIO; 338 break; 339 case 1: 340 if (mode == WDT_ENABLE) { 341 wh->wh_wdog_armed = 1; 342 error = 0; 343 } else 344 error = EIO; 345 break; 346 } 347 348 splx(s); 349 return(error); 350 } 351 352 int 353 weasel_isa_wdog_query_state(struct weasel_handle *wh) 354 { 355 int timeout, reg; 356 357 bus_space_write_1(wh->wh_st, wh->wh_sh, 358 WEASEL_MISC_COMMAND, OS_WDT_QUERY); 359 for (timeout = 0; timeout < 1500; timeout++) { 360 delay(1000); 361 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 362 WEASEL_MISC_COMMAND); 363 if (reg == OS_READY) 364 break; 365 } 366 return(bus_space_read_1(wh->wh_st, wh->wh_sh, WEASEL_MISC_RESPONSE)); 367 } 368