1 /* $NetBSD: weasel_isa.c,v 1.6 2015/08/20 14:40:18 christos 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.6 2015/08/20 14:40:18 christos 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 <sys/bus.h> 53 54 #include <dev/isa/weaselreg.h> 55 #include <dev/isa/weaselvar.h> 56 57 #include <dev/sysmon/sysmonvar.h> 58 59 #include "ioconf.h" 60 61 int weasel_isa_wdog_setmode(struct sysmon_wdog *); 62 int weasel_isa_wdog_tickle(struct sysmon_wdog *); 63 int weasel_isa_wdog_arm_disarm(struct weasel_handle *, u_int8_t); 64 int weasel_isa_wdog_query_state(struct weasel_handle *); 65 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", device_xname(wh->wh_parent), 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, ", device_xname(wh->wh_parent)); 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", device_xname(wh->wh_parent), 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 aprint_error_dev(wh->wh_parent, "unable to register PC-Weasel watchdog " 216 "with sysmon\n"); 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 aprint_error_dev(wh->wh_parent, "Watchdog timer disabled on PC/Weasel! Disarming wdog.\n"); 270 wh->wh_wdog_armed = 0; 271 error = 1; 272 } 273 splx(s); 274 275 return (error); 276 } 277 278 int 279 weasel_isa_wdog_arm_disarm(struct weasel_handle *wh, u_int8_t mode) 280 { 281 u_int8_t reg; 282 int timeout; 283 int s, x; 284 int error = 0; 285 286 s = splhigh(); 287 288 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_SEMAPHORE, 289 WDT_ATTENTION); 290 for (timeout = 5000; timeout; timeout--) { 291 delay(1500); 292 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 293 WEASEL_WDT_SEMAPHORE); 294 if (reg == WDT_OK) 295 break; 296 } 297 if (timeout == 0) { 298 splx(s); 299 return(EIO); 300 } 301 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_SEMAPHORE, mode); 302 for (timeout = 500 ; timeout; timeout--) { 303 delay(1500); 304 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 305 WEASEL_WDT_SEMAPHORE); 306 if (reg != mode) 307 break; 308 } 309 if (timeout == 0) { 310 splx(s); 311 return(EIO); 312 } 313 bus_space_write_1(wh->wh_st, wh->wh_sh, WEASEL_WDT_SEMAPHORE, ~reg); 314 for (timeout = 500; timeout; timeout--) { 315 delay(1500); 316 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 317 WEASEL_WDT_SEMAPHORE); 318 if (reg == WDT_OK) 319 break; 320 } 321 322 /* 323 * Ensure that the Weasel thinks it's in the same mode we want it to 324 * be in. EIO if not. 325 */ 326 x = weasel_isa_wdog_query_state(wh); 327 switch (x) { 328 case -1: 329 error = EIO; 330 break; 331 case 0: 332 if (mode == WDT_DISABLE) { 333 wh->wh_wdog_armed = 0; 334 error = 0; 335 } else 336 error = EIO; 337 break; 338 case 1: 339 if (mode == WDT_ENABLE) { 340 wh->wh_wdog_armed = 1; 341 error = 0; 342 } else 343 error = EIO; 344 break; 345 } 346 347 splx(s); 348 return(error); 349 } 350 351 int 352 weasel_isa_wdog_query_state(struct weasel_handle *wh) 353 { 354 int timeout, reg; 355 356 bus_space_write_1(wh->wh_st, wh->wh_sh, 357 WEASEL_MISC_COMMAND, OS_WDT_QUERY); 358 for (timeout = 0; timeout < 1500; timeout++) { 359 delay(1000); 360 reg = bus_space_read_1(wh->wh_st, wh->wh_sh, 361 WEASEL_MISC_COMMAND); 362 if (reg == OS_READY) 363 break; 364 } 365 return(bus_space_read_1(wh->wh_st, wh->wh_sh, WEASEL_MISC_RESPONSE)); 366 } 367