aic7xxx_seeprom.c revision 1.6
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
64static void configure_termination(struct ahc_softc *,
65				  struct seeprom_descriptor *, u_int, u_int *);
66
67static void ahc_new_term_detect(struct ahc_softc *, int *, int *, int *,
68				   int *, int *);
69static void aic787X_cable_detect(struct ahc_softc *, int *, int *, int *,
70				 int *);
71static void aic785X_cable_detect(struct ahc_softc *, int *, int *, int *);
72static int acquire_seeprom(struct ahc_softc *, struct seeprom_descriptor *);
73static void release_seeprom(struct seeprom_descriptor *);
74static void write_brdctl(struct ahc_softc *, u_int8_t);
75static 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 */
81void
82check_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
286static void
287configure_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
497static void
498ahc_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
519static void
520aic787X_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
560static void
561aic785X_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
575static int
576acquire_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
603static void
604release_seeprom(struct seeprom_descriptor *sd)
605{
606	/* Release access to the memory port and the serial EEPROM. */
607	SEEPROM_OUTB(sd, 0);
608}
609
610static void
611write_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
642static u_int8_t
643read_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