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