1 /* $NetBSD: aic7xxx_seeprom.c,v 1.5 2001/04/30 03:45:35 lukem Exp $ */ 2 3 /* 4 * Product specific probe and attach routines for: 5 * 3940, 2940, aic7895, aic7890, aic7880, 6 * aic7870, aic7860 and aic7850 SCSI controllers 7 * 8 * These are the SEEPROM-reading functions only. They were split off from 9 * the PCI-specific support by Jason R. Thorpe <thorpej@netbsd.org>. 10 * 11 * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 Justin T. Gibbs. 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions, and the following disclaimer, 19 * without modification. 20 * 2. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * Alternatively, this software may be distributed under the terms of the 24 * the GNU Public License ("GPL"). 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 30 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * $FreeBSD: src/sys/dev/aic7xxx/ahc_pci.c,v 1.27 2000/01/10 01:47:51 gibbs Exp 39 $ 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/malloc.h> 45 #include <sys/kernel.h> 46 #include <sys/queue.h> 47 #include <sys/device.h> 48 #include <sys/reboot.h> /* for AB_* needed by bootverbose */ 49 50 #include <machine/bus.h> 51 #include <machine/intr.h> 52 53 #include <dev/scsipi/scsi_all.h> 54 #include <dev/scsipi/scsipi_all.h> 55 #include <dev/scsipi/scsiconf.h> 56 57 #include <dev/microcode/aic7xxx/aic7xxx_reg.h> 58 #include <dev/ic/aic7xxxvar.h> 59 #include <dev/ic/smc93cx6var.h> 60 61 static void configure_termination(struct ahc_softc *, 62 struct seeprom_descriptor *, u_int, u_int *); 63 64 static void ahc_new_term_detect(struct ahc_softc *, int *, int *, int *, 65 int *, int *); 66 static void aic787X_cable_detect(struct ahc_softc *, int *, int *, int *, 67 int *); 68 static void aic785X_cable_detect(struct ahc_softc *, int *, int *, int *); 69 static int acquire_seeprom(struct ahc_softc *, struct seeprom_descriptor *); 70 static void release_seeprom(struct seeprom_descriptor *); 71 static void write_brdctl(struct ahc_softc *, u_int8_t); 72 static u_int8_t read_brdctl(struct ahc_softc *); 73 74 /* 75 * Check the external port logic for a serial eeprom 76 * and termination/cable detection contrls. 77 */ 78 void 79 check_extport(struct ahc_softc *ahc, u_int *sxfrctl1) 80 { 81 struct seeprom_descriptor sd; 82 struct seeprom_config sc; 83 u_int scsi_conf; 84 u_int adapter_control; 85 int have_seeprom; 86 int have_autoterm; 87 88 sd.sd_tag = ahc->tag; 89 sd.sd_bsh = ahc->bsh; 90 sd.sd_control_offset = SEECTL; 91 sd.sd_status_offset = SEECTL; 92 sd.sd_dataout_offset = SEECTL; 93 94 /* 95 * For some multi-channel devices, the c46 is simply too 96 * small to work. For the other controller types, we can 97 * get our information from either SEEPROM type. Set the 98 * type to start our probe with accordingly. 99 */ 100 if (ahc->flags & AHC_LARGE_SEEPROM) 101 sd.sd_chip = C56_66; 102 else 103 sd.sd_chip = C46; 104 105 sd.sd_MS = SEEMS; 106 sd.sd_RDY = SEERDY; 107 sd.sd_CS = SEECS; 108 sd.sd_CK = SEECK; 109 sd.sd_DO = SEEDO; 110 sd.sd_DI = SEEDI; 111 112 have_seeprom = acquire_seeprom(ahc, &sd); 113 if (have_seeprom) { 114 115 if (bootverbose) 116 printf("%s: Reading SEEPROM...", ahc_name(ahc)); 117 118 for (;;) { 119 bus_size_t start_addr; 120 121 start_addr = 32 * (ahc->channel - 'A'); 122 123 have_seeprom = read_seeprom(&sd, (u_int16_t *)&sc, 124 start_addr, sizeof(sc)/2); 125 126 if (have_seeprom) { 127 /* Check checksum */ 128 int i; 129 int maxaddr; 130 u_int32_t checksum; 131 u_int16_t *scarray; 132 133 maxaddr = (sizeof(sc)/2) - 1; 134 checksum = 0; 135 scarray = (u_int16_t *)≻ 136 137 for (i = 0; i < maxaddr; i++) 138 checksum = checksum + scarray[i]; 139 if (checksum == 0 140 || (checksum & 0xFFFF) != sc.checksum) { 141 if (bootverbose && sd.sd_chip == C56_66) 142 printf ("checksum error\n"); 143 have_seeprom = 0; 144 } else { 145 if (bootverbose) 146 printf("done.\n"); 147 break; 148 } 149 } 150 151 if (sd.sd_chip == C56_66) 152 break; 153 sd.sd_chip = C56_66; 154 } 155 } 156 157 if (!have_seeprom) { 158 if (bootverbose) 159 printf("%s: No SEEPROM available\n", ahc_name(ahc)); 160 ahc->flags |= AHC_USEDEFAULTS; 161 } else { 162 /* 163 * Put the data we've collected down into SRAM 164 * where ahc_init will find it. 165 */ 166 int i; 167 int max_targ = sc.max_targets & CFMAXTARG; 168 u_int16_t discenable; 169 u_int16_t ultraenb; 170 171 discenable = 0; 172 ultraenb = 0; 173 if ((sc.adapter_control & CFULTRAEN) != 0) { 174 /* 175 * Determine if this adapter has a "newstyle" 176 * SEEPROM format. 177 */ 178 for (i = 0; i < max_targ; i++) { 179 if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0){ 180 ahc->flags |= AHC_NEWEEPROM_FMT; 181 break; 182 } 183 } 184 } 185 186 for (i = 0; i < max_targ; i++) { 187 u_int scsirate; 188 u_int16_t target_mask; 189 190 target_mask = 0x01 << i; 191 if (sc.device_flags[i] & CFDISC) 192 discenable |= target_mask; 193 if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) { 194 if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0) 195 ultraenb |= target_mask; 196 } else if ((sc.adapter_control & CFULTRAEN) != 0) { 197 ultraenb |= target_mask; 198 } 199 if ((sc.device_flags[i] & CFXFER) == 0x04 200 && (ultraenb & target_mask) != 0) { 201 /* Treat 10MHz as a non-ultra speed */ 202 sc.device_flags[i] &= ~CFXFER; 203 ultraenb &= ~target_mask; 204 } 205 if ((ahc->features & AHC_ULTRA2) != 0) { 206 u_int offset; 207 208 if (sc.device_flags[i] & CFSYNCH) 209 offset = MAX_OFFSET_ULTRA2; 210 else 211 offset = 0; 212 ahc_outb(ahc, TARG_OFFSET + i, offset); 213 214 scsirate = (sc.device_flags[i] & CFXFER) 215 | ((ultraenb & target_mask) 216 ? 0x8 : 0x0); 217 if (sc.device_flags[i] & CFWIDEB) 218 scsirate |= WIDEXFER; 219 } else { 220 scsirate = (sc.device_flags[i] & CFXFER) << 4; 221 if (sc.device_flags[i] & CFSYNCH) 222 scsirate |= SOFS; 223 if (sc.device_flags[i] & CFWIDEB) 224 scsirate |= WIDEXFER; 225 } 226 ahc_outb(ahc, TARG_SCSIRATE + i, scsirate); 227 } 228 ahc->our_id = sc.brtime_id & CFSCSIID; 229 230 scsi_conf = (ahc->our_id & 0x7); 231 if (sc.adapter_control & CFSPARITY) 232 scsi_conf |= ENSPCHK; 233 if (sc.adapter_control & CFRESETB) 234 scsi_conf |= RESET_SCSI; 235 236 if (sc.bios_control & CFEXTEND) 237 ahc->flags |= AHC_EXTENDED_TRANS_A; 238 if (ahc->features & AHC_ULTRA 239 && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) { 240 /* Should we enable Ultra mode? */ 241 if (!(sc.adapter_control & CFULTRAEN)) 242 /* Treat us as a non-ultra card */ 243 ultraenb = 0; 244 } 245 /* Set SCSICONF info */ 246 ahc_outb(ahc, SCSICONF, scsi_conf); 247 ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff)); 248 ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff)); 249 ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff); 250 ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff); 251 } 252 253 /* 254 * Cards that have the external logic necessary to talk to 255 * a SEEPROM, are almost certain to have the remaining logic 256 * necessary for auto-termination control. This assumption 257 * hasn't failed yet... 258 */ 259 have_autoterm = have_seeprom; 260 if (have_seeprom) 261 adapter_control = sc.adapter_control; 262 else 263 adapter_control = CFAUTOTERM; 264 265 /* 266 * Some low-cost chips have SEEPROM and auto-term control built 267 * in, instead of using a GAL. They can tell us directly 268 * if the termination logic is enabled. 269 */ 270 if ((ahc->features & AHC_SPIOCAP) != 0) { 271 if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) != 0) 272 have_autoterm = TRUE; 273 else 274 have_autoterm = FALSE; 275 } 276 277 if (have_autoterm) 278 configure_termination(ahc, &sd, adapter_control, sxfrctl1); 279 280 release_seeprom(&sd); 281 } 282 283 static void 284 configure_termination(struct ahc_softc *ahc, 285 struct seeprom_descriptor *sd, 286 u_int adapter_control, 287 u_int *sxfrctl1) 288 { 289 u_int8_t brddat; 290 291 brddat = 0; 292 293 /* 294 * Update the settings in sxfrctl1 to match the 295 * termination settings 296 */ 297 *sxfrctl1 = 0; 298 299 /* 300 * SEECS must be on for the GALS to latch 301 * the data properly. Be sure to leave MS 302 * on or we will release the seeprom. 303 */ 304 SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS); 305 if ((adapter_control & CFAUTOTERM) != 0 306 || (ahc->features & AHC_NEW_TERMCTL) != 0) { 307 int internal50_present; 308 int internal68_present; 309 int externalcable_present; 310 int eeprom_present; 311 int enableSEC_low; 312 int enableSEC_high; 313 int enablePRI_low; 314 int enablePRI_high; 315 316 enableSEC_low = 0; 317 enableSEC_high = 0; 318 enablePRI_low = 0; 319 enablePRI_high = 0; 320 if ((ahc->features & AHC_NEW_TERMCTL) != 0) { 321 ahc_new_term_detect(ahc, &enableSEC_low, 322 &enableSEC_high, 323 &enablePRI_low, 324 &enablePRI_high, 325 &eeprom_present); 326 if ((adapter_control & CFSEAUTOTERM) == 0) { 327 if (bootverbose) 328 printf("%s: Manual SE Termination\n", 329 ahc_name(ahc)); 330 enableSEC_low = (adapter_control & CFSTERM); 331 enableSEC_high = (adapter_control & CFWSTERM); 332 } 333 if ((adapter_control & CFAUTOTERM) == 0) { 334 if (bootverbose) 335 printf("%s: Manual LVD Termination\n", 336 ahc_name(ahc)); 337 enablePRI_low = enablePRI_high = 338 (adapter_control & CFLVDSTERM); 339 } 340 /* Make the table calculations below happy */ 341 internal50_present = 0; 342 internal68_present = 1; 343 externalcable_present = 1; 344 } else if ((ahc->features & AHC_SPIOCAP) != 0) { 345 aic785X_cable_detect(ahc, &internal50_present, 346 &externalcable_present, 347 &eeprom_present); 348 } else { 349 aic787X_cable_detect(ahc, &internal50_present, 350 &internal68_present, 351 &externalcable_present, 352 &eeprom_present); 353 } 354 355 if ((ahc->features & AHC_WIDE) == 0) 356 internal68_present = 0; 357 358 if (bootverbose) { 359 if ((ahc->features & AHC_ULTRA2) == 0) { 360 printf("%s: internal 50 cable %s present, " 361 "internal 68 cable %s present\n", 362 ahc_name(ahc), 363 internal50_present ? "is":"not", 364 internal68_present ? "is":"not"); 365 366 printf("%s: external cable %s present\n", 367 ahc_name(ahc), 368 externalcable_present ? "is":"not"); 369 } 370 printf("%s: BIOS eeprom %s present\n", 371 ahc_name(ahc), eeprom_present ? "is" : "not"); 372 } 373 374 if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) { 375 /* 376 * The 50 pin connector is a separate bus, 377 * so force it to always be terminated. 378 * In the future, perform current sensing 379 * to determine if we are in the middle of 380 * a properly terminated bus. 381 */ 382 internal50_present = 0; 383 } 384 385 /* 386 * Now set the termination based on what 387 * we found. 388 * Flash Enable = BRDDAT7 389 * Secondary High Term Enable = BRDDAT6 390 * Secondary Low Term Enable = BRDDAT5 (7890) 391 * Primary High Term Enable = BRDDAT4 (7890) 392 */ 393 if ((ahc->features & AHC_ULTRA2) == 0 394 && (internal50_present != 0) 395 && (internal68_present != 0) 396 && (externalcable_present != 0)) { 397 printf("%s: Illegal cable configuration!!. " 398 "Only two connectors on the " 399 "adapter may be used at a " 400 "time!\n", ahc_name(ahc)); 401 } 402 403 if ((ahc->features & AHC_WIDE) != 0 404 && ((externalcable_present == 0) 405 || (internal68_present == 0) 406 || (enableSEC_high != 0))) { 407 brddat |= BRDDAT6; 408 if (bootverbose) { 409 if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) 410 printf("%s: 68 pin termination " 411 "Enabled\n", ahc_name(ahc)); 412 else 413 printf("%s: %sHigh byte termination " 414 "Enabled\n", ahc_name(ahc), 415 enableSEC_high ? "Secondary " 416 : ""); 417 } 418 } 419 420 if (((internal50_present ? 1 : 0) 421 + (internal68_present ? 1 : 0) 422 + (externalcable_present ? 1 : 0)) <= 1 423 || (enableSEC_low != 0)) { 424 if ((ahc->features & AHC_ULTRA2) != 0) 425 brddat |= BRDDAT5; 426 else 427 *sxfrctl1 |= STPWEN; 428 if (bootverbose) { 429 if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) 430 printf("%s: 50 pin termination " 431 "Enabled\n", ahc_name(ahc)); 432 else 433 printf("%s: %sLow byte termination " 434 "Enabled\n", ahc_name(ahc), 435 enableSEC_low ? "Secondary " 436 : ""); 437 } 438 } 439 440 if (enablePRI_low != 0) { 441 *sxfrctl1 |= STPWEN; 442 if (bootverbose) 443 printf("%s: Primary Low Byte termination " 444 "Enabled\n", ahc_name(ahc)); 445 } 446 447 /* 448 * Setup STPWEN before setting up the rest of 449 * the termination per the tech note on the U160 cards. 450 */ 451 ahc_outb(ahc, SXFRCTL1, *sxfrctl1); 452 453 if (enablePRI_high != 0) { 454 brddat |= BRDDAT4; 455 if (bootverbose) 456 printf("%s: Primary High Byte " 457 "termination Enabled\n", 458 ahc_name(ahc)); 459 } 460 461 write_brdctl(ahc, brddat); 462 463 } else { 464 if ((adapter_control & CFSTERM) != 0) { 465 *sxfrctl1 |= STPWEN; 466 467 if (bootverbose) 468 printf("%s: %sLow byte termination Enabled\n", 469 ahc_name(ahc), 470 (ahc->features & AHC_ULTRA2) ? "Primary " 471 : ""); 472 } 473 474 if ((adapter_control & CFWSTERM) != 0) { 475 brddat |= BRDDAT6; 476 if (bootverbose) 477 printf("%s: %sHigh byte termination Enabled\n", 478 ahc_name(ahc), 479 (ahc->features & AHC_ULTRA2) 480 ? "Secondary " : ""); 481 } 482 483 /* 484 * Setup STPWEN before setting up the rest of 485 * the termination per the tech note on the U160 cards. 486 */ 487 ahc_outb(ahc, SXFRCTL1, *sxfrctl1); 488 489 write_brdctl(ahc, brddat); 490 } 491 SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */ 492 } 493 494 static void 495 ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low, 496 int *enableSEC_high, int *enablePRI_low, 497 int *enablePRI_high, int *eeprom_present) 498 { 499 u_int8_t brdctl; 500 501 /* 502 * BRDDAT7 = Eeprom 503 * BRDDAT6 = Enable Secondary High Byte termination 504 * BRDDAT5 = Enable Secondary Low Byte termination 505 * BRDDAT4 = Enable Primary high byte termination 506 * BRDDAT3 = Enable Primary low byte termination 507 */ 508 brdctl = read_brdctl(ahc); 509 *eeprom_present = brdctl & BRDDAT7; 510 *enableSEC_high = (brdctl & BRDDAT6); 511 *enableSEC_low = (brdctl & BRDDAT5); 512 *enablePRI_high = (brdctl & BRDDAT4); 513 *enablePRI_low = (brdctl & BRDDAT3); 514 } 515 516 static void 517 aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present, 518 int *internal68_present, int *externalcable_present, 519 int *eeprom_present) 520 { 521 u_int8_t brdctl; 522 523 /* 524 * First read the status of our cables. 525 * Set the rom bank to 0 since the 526 * bank setting serves as a multiplexor 527 * for the cable detection logic. 528 * BRDDAT5 controls the bank switch. 529 */ 530 write_brdctl(ahc, 0); 531 532 /* 533 * Now read the state of the internal 534 * connectors. BRDDAT6 is INT50 and 535 * BRDDAT7 is INT68. 536 */ 537 brdctl = read_brdctl(ahc); 538 *internal50_present = !(brdctl & BRDDAT6); 539 *internal68_present = !(brdctl & BRDDAT7); 540 541 /* 542 * Set the rom bank to 1 and determine 543 * the other signals. 544 */ 545 write_brdctl(ahc, BRDDAT5); 546 547 /* 548 * Now read the state of the external 549 * connectors. BRDDAT6 is EXT68 and 550 * BRDDAT7 is EPROMPS. 551 */ 552 brdctl = read_brdctl(ahc); 553 *externalcable_present = !(brdctl & BRDDAT6); 554 *eeprom_present = brdctl & BRDDAT7; 555 } 556 557 static void 558 aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present, 559 int *externalcable_present, int *eeprom_present) 560 { 561 u_int8_t brdctl; 562 563 ahc_outb(ahc, BRDCTL, BRDRW|BRDCS); 564 ahc_outb(ahc, BRDCTL, 0); 565 brdctl = ahc_inb(ahc, BRDCTL); 566 *internal50_present = !(brdctl & BRDDAT5); 567 *externalcable_present = !(brdctl & BRDDAT6); 568 569 *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) != 0; 570 } 571 572 static int 573 acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd) 574 { 575 int wait; 576 577 if ((ahc->features & AHC_SPIOCAP) != 0 578 && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0) 579 return (0); 580 581 /* 582 * Request access of the memory port. When access is 583 * granted, SEERDY will go high. We use a 100 msec 584 * timeout which should be near 100 msecs more than 585 * is needed. Reason: after the chip reset, there 586 * should be no contention. 587 */ 588 SEEPROM_OUTB(sd, sd->sd_MS); 589 wait = 100; /* 100 msec timeout */ 590 while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) { 591 DELAY(1000); /* delay 1 msec */ 592 } 593 if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) { 594 SEEPROM_OUTB(sd, 0); 595 return (0); 596 } 597 return(1); 598 } 599 600 static void 601 release_seeprom(struct seeprom_descriptor *sd) 602 { 603 /* Release access to the memory port and the serial EEPROM. */ 604 SEEPROM_OUTB(sd, 0); 605 } 606 607 static void 608 write_brdctl(struct ahc_softc *ahc, u_int8_t value) 609 { 610 u_int8_t brdctl; 611 612 if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { 613 brdctl = BRDSTB; 614 if (ahc->channel == 'B') 615 brdctl |= BRDCS; 616 } else if ((ahc->features & AHC_ULTRA2) != 0) { 617 brdctl = 0; 618 } else { 619 brdctl = BRDSTB|BRDCS; 620 } 621 ahc_outb(ahc, BRDCTL, brdctl); 622 DELAY(20); 623 brdctl |= value; 624 ahc_outb(ahc, BRDCTL, brdctl); 625 DELAY(20); 626 if ((ahc->features & AHC_ULTRA2) != 0) 627 brdctl |= BRDSTB_ULTRA2; 628 else 629 brdctl &= ~BRDSTB; 630 ahc_outb(ahc, BRDCTL, brdctl); 631 DELAY(20); 632 if ((ahc->features & AHC_ULTRA2) != 0) 633 brdctl = 0; 634 else 635 brdctl &= ~BRDCS; 636 ahc_outb(ahc, BRDCTL, brdctl); 637 } 638 639 static u_int8_t 640 read_brdctl(struct ahc_softc *ahc) 641 { 642 u_int8_t brdctl; 643 u_int8_t value; 644 645 if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { 646 brdctl = BRDRW; 647 if (ahc->channel == 'B') 648 brdctl |= BRDCS; 649 } else if ((ahc->features & AHC_ULTRA2) != 0) { 650 brdctl = BRDRW_ULTRA2; 651 } else { 652 brdctl = BRDRW|BRDCS; 653 } 654 ahc_outb(ahc, BRDCTL, brdctl); 655 DELAY(20); 656 value = ahc_inb(ahc, BRDCTL); 657 ahc_outb(ahc, BRDCTL, 0); 658 return (value); 659 } 660