xref: /freebsd/sys/dev/aic7xxx/aic7770.c (revision 717d424718c4dd7a8520354fc0cc683e82a3e813)
1717d4247SJustin T. Gibbs /*
2717d4247SJustin T. Gibbs  * Product specific probe and attach routines for:
3717d4247SJustin T. Gibbs  * 	27/284X and aic7770 motherboard SCSI controllers
4717d4247SJustin T. Gibbs  *
5717d4247SJustin T. Gibbs  * Copyright (c) 1994, 1995, 1996, 1997, 1998, 2000 Justin T. Gibbs.
6717d4247SJustin T. Gibbs  * All rights reserved.
7717d4247SJustin T. Gibbs  *
8717d4247SJustin T. Gibbs  * Redistribution and use in source and binary forms, with or without
9717d4247SJustin T. Gibbs  * modification, are permitted provided that the following conditions
10717d4247SJustin T. Gibbs  * are met:
11717d4247SJustin T. Gibbs  * 1. Redistributions of source code must retain the above copyright
12717d4247SJustin T. Gibbs  *    notice immediately at the beginning of the file, without modification,
13717d4247SJustin T. Gibbs  *    this list of conditions, and the following disclaimer.
14717d4247SJustin T. Gibbs  * 2. The name of the author may not be used to endorse or promote products
15717d4247SJustin T. Gibbs  *    derived from this software without specific prior written permission.
16717d4247SJustin T. Gibbs  *
17717d4247SJustin T. Gibbs  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18717d4247SJustin T. Gibbs  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19717d4247SJustin T. Gibbs  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20717d4247SJustin T. Gibbs  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21717d4247SJustin T. Gibbs  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22717d4247SJustin T. Gibbs  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23717d4247SJustin T. Gibbs  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24717d4247SJustin T. Gibbs  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25717d4247SJustin T. Gibbs  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26717d4247SJustin T. Gibbs  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27717d4247SJustin T. Gibbs  * SUCH DAMAGE.
28717d4247SJustin T. Gibbs  *
29717d4247SJustin T. Gibbs  * $Id$
30717d4247SJustin T. Gibbs  *
31717d4247SJustin T. Gibbs  * $FreeBSD$
32717d4247SJustin T. Gibbs  */
33717d4247SJustin T. Gibbs 
34717d4247SJustin T. Gibbs #ifdef	__linux__
35717d4247SJustin T. Gibbs #include "aic7xxx_linux.h"
36717d4247SJustin T. Gibbs #include "aic7xxx_inline.h"
37717d4247SJustin T. Gibbs #include "aic7xxx_93cx6.h"
38717d4247SJustin T. Gibbs #endif
39717d4247SJustin T. Gibbs 
40717d4247SJustin T. Gibbs #ifdef	__FreeBSD__
41717d4247SJustin T. Gibbs #include <dev/aic7xxx/aic7xxx_freebsd.h>
42717d4247SJustin T. Gibbs #include <dev/aic7xxx/aic7xxx_inline.h>
43717d4247SJustin T. Gibbs #include <dev/aic7xxx/aic7xxx_93cx6.h>
44717d4247SJustin T. Gibbs #endif
45717d4247SJustin T. Gibbs 
46717d4247SJustin T. Gibbs #define ID_AIC7770	0x04907770
47717d4247SJustin T. Gibbs #define ID_AHA_274x	0x04907771
48717d4247SJustin T. Gibbs #define ID_AHA_284xB	0x04907756 /* BIOS enabled */
49717d4247SJustin T. Gibbs #define ID_AHA_284x	0x04907757 /* BIOS disabled*/
50717d4247SJustin T. Gibbs 
51717d4247SJustin T. Gibbs static void aha2840_load_seeprom(struct ahc_softc *ahc);
52717d4247SJustin T. Gibbs static ahc_device_setup_t ahc_aic7770_VL_setup;
53717d4247SJustin T. Gibbs static ahc_device_setup_t ahc_aic7770_EISA_setup;;
54717d4247SJustin T. Gibbs static ahc_device_setup_t ahc_aic7770_setup;
55717d4247SJustin T. Gibbs 
56717d4247SJustin T. Gibbs 
57717d4247SJustin T. Gibbs struct aic7770_identity aic7770_ident_table [] =
58717d4247SJustin T. Gibbs {
59717d4247SJustin T. Gibbs 	{
60717d4247SJustin T. Gibbs 		ID_AHA_274x,
61717d4247SJustin T. Gibbs 		0xFFFFFFFF,
62717d4247SJustin T. Gibbs 		"Adaptec 274X SCSI adapter",
63717d4247SJustin T. Gibbs 		ahc_aic7770_EISA_setup
64717d4247SJustin T. Gibbs 	},
65717d4247SJustin T. Gibbs 	{
66717d4247SJustin T. Gibbs 		ID_AHA_284xB,
67717d4247SJustin T. Gibbs 		0xFFFFFFFE,
68717d4247SJustin T. Gibbs 		"Adaptec 284X SCSI adapter",
69717d4247SJustin T. Gibbs 		ahc_aic7770_VL_setup
70717d4247SJustin T. Gibbs 	},
71717d4247SJustin T. Gibbs 	/* Generic chip probes for devices we don't know 'exactly' */
72717d4247SJustin T. Gibbs 	{
73717d4247SJustin T. Gibbs 		ID_AIC7770,
74717d4247SJustin T. Gibbs 		0xFFFFFFFF,
75717d4247SJustin T. Gibbs 		"Adaptec aic7770 SCSI adapter",
76717d4247SJustin T. Gibbs 		ahc_aic7770_EISA_setup
77717d4247SJustin T. Gibbs 	}
78717d4247SJustin T. Gibbs };
79717d4247SJustin T. Gibbs const int ahc_num_aic7770_devs = NUM_ELEMENTS(aic7770_ident_table);
80717d4247SJustin T. Gibbs 
81717d4247SJustin T. Gibbs struct aic7770_identity *
82717d4247SJustin T. Gibbs aic7770_find_device(uint32_t id)
83717d4247SJustin T. Gibbs {
84717d4247SJustin T. Gibbs 	struct	aic7770_identity *entry;
85717d4247SJustin T. Gibbs 	int	i;
86717d4247SJustin T. Gibbs 
87717d4247SJustin T. Gibbs 	for (i = 0; i < ahc_num_aic7770_devs; i++) {
88717d4247SJustin T. Gibbs 		entry = &aic7770_ident_table[i];
89717d4247SJustin T. Gibbs 		if (entry->full_id == (id & entry->id_mask))
90717d4247SJustin T. Gibbs 			return (entry);
91717d4247SJustin T. Gibbs 	}
92717d4247SJustin T. Gibbs 	return (NULL);
93717d4247SJustin T. Gibbs }
94717d4247SJustin T. Gibbs 
95717d4247SJustin T. Gibbs int
96717d4247SJustin T. Gibbs aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry)
97717d4247SJustin T. Gibbs {
98717d4247SJustin T. Gibbs 	struct	ahc_probe_config probe_config;
99717d4247SJustin T. Gibbs 	int	error;
100717d4247SJustin T. Gibbs 	u_int	hostconf;
101717d4247SJustin T. Gibbs 
102717d4247SJustin T. Gibbs 	ahc_init_probe_config(&probe_config);
103717d4247SJustin T. Gibbs 	error = entry->setup(ahc->dev_softc, &probe_config);
104717d4247SJustin T. Gibbs 	if (error != 0)
105717d4247SJustin T. Gibbs 		return (error);
106717d4247SJustin T. Gibbs 
107717d4247SJustin T. Gibbs 	error = aic7770_map_registers(ahc);
108717d4247SJustin T. Gibbs 	if (error != 0)
109717d4247SJustin T. Gibbs 		return (error);
110717d4247SJustin T. Gibbs 
111717d4247SJustin T. Gibbs 	probe_config.description = entry->name;
112717d4247SJustin T. Gibbs 	error = ahc_softc_init(ahc, &probe_config);
113717d4247SJustin T. Gibbs 
114717d4247SJustin T. Gibbs 	error = aic7770_map_int(ahc);
115717d4247SJustin T. Gibbs 	if (error != 0)
116717d4247SJustin T. Gibbs 		return (error);
117717d4247SJustin T. Gibbs 
118717d4247SJustin T. Gibbs 	error = ahc_reset(ahc);
119717d4247SJustin T. Gibbs 	if (error != 0)
120717d4247SJustin T. Gibbs 		return (error);
121717d4247SJustin T. Gibbs 
122717d4247SJustin T. Gibbs 	switch (probe_config.chip & (AHC_EISA|AHC_VL)) {
123717d4247SJustin T. Gibbs 	case AHC_EISA:
124717d4247SJustin T. Gibbs 	{
125717d4247SJustin T. Gibbs 		u_int biosctrl;
126717d4247SJustin T. Gibbs 		u_int scsiconf;
127717d4247SJustin T. Gibbs 		u_int scsiconf1;
128717d4247SJustin T. Gibbs 
129717d4247SJustin T. Gibbs 		biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
130717d4247SJustin T. Gibbs 		scsiconf = ahc_inb(ahc, SCSICONF);
131717d4247SJustin T. Gibbs 		scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
132717d4247SJustin T. Gibbs 
133717d4247SJustin T. Gibbs 		/* Get the primary channel information */
134717d4247SJustin T. Gibbs 		if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
135717d4247SJustin T. Gibbs 			ahc->flags |= AHC_CHANNEL_B_PRIMARY;
136717d4247SJustin T. Gibbs 
137717d4247SJustin T. Gibbs 		if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
138717d4247SJustin T. Gibbs 			ahc->flags |= AHC_USEDEFAULTS;
139717d4247SJustin T. Gibbs 		} else {
140717d4247SJustin T. Gibbs 			if ((ahc->features & AHC_WIDE) != 0) {
141717d4247SJustin T. Gibbs 				ahc->our_id = scsiconf1 & HWSCSIID;
142717d4247SJustin T. Gibbs 				if (scsiconf & TERM_ENB)
143717d4247SJustin T. Gibbs 					ahc->flags |= AHC_TERM_ENB_A;
144717d4247SJustin T. Gibbs 			} else {
145717d4247SJustin T. Gibbs 				ahc->our_id = scsiconf & HSCSIID;
146717d4247SJustin T. Gibbs 				ahc->our_id_b = scsiconf1 & HSCSIID;
147717d4247SJustin T. Gibbs 				if (scsiconf & TERM_ENB)
148717d4247SJustin T. Gibbs 					ahc->flags |= AHC_TERM_ENB_A;
149717d4247SJustin T. Gibbs 				if (scsiconf1 & TERM_ENB)
150717d4247SJustin T. Gibbs 					ahc->flags |= AHC_TERM_ENB_B;
151717d4247SJustin T. Gibbs 			}
152717d4247SJustin T. Gibbs 		}
153717d4247SJustin T. Gibbs 		/*
154717d4247SJustin T. Gibbs 		 * We have no way to tell, so assume extended
155717d4247SJustin T. Gibbs 		 * translation is enabled.
156717d4247SJustin T. Gibbs 		 */
157717d4247SJustin T. Gibbs 		ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
158717d4247SJustin T. Gibbs 		break;
159717d4247SJustin T. Gibbs 	}
160717d4247SJustin T. Gibbs 	case AHC_VL:
161717d4247SJustin T. Gibbs 	{
162717d4247SJustin T. Gibbs 		aha2840_load_seeprom(ahc);
163717d4247SJustin T. Gibbs 		break;
164717d4247SJustin T. Gibbs 	}
165717d4247SJustin T. Gibbs 	default:
166717d4247SJustin T. Gibbs 		break;
167717d4247SJustin T. Gibbs 	}
168717d4247SJustin T. Gibbs 
169717d4247SJustin T. Gibbs 	/*
170717d4247SJustin T. Gibbs 	 * Ensure autoflush is enabled
171717d4247SJustin T. Gibbs 	 */
172717d4247SJustin T. Gibbs 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
173717d4247SJustin T. Gibbs 
174717d4247SJustin T. Gibbs 	/* Setup the FIFO threshold and the bus off time */
175717d4247SJustin T. Gibbs 	hostconf = ahc_inb(ahc, HOSTCONF);
176717d4247SJustin T. Gibbs 	ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
177717d4247SJustin T. Gibbs 	ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
178717d4247SJustin T. Gibbs 
179717d4247SJustin T. Gibbs 	/*
180717d4247SJustin T. Gibbs 	 * Generic aic7xxx initialization.
181717d4247SJustin T. Gibbs 	 */
182717d4247SJustin T. Gibbs 	error = ahc_init(ahc);
183717d4247SJustin T. Gibbs 	if (error != 0)
184717d4247SJustin T. Gibbs 		return (error);
185717d4247SJustin T. Gibbs 
186717d4247SJustin T. Gibbs 	/*
187717d4247SJustin T. Gibbs 	 * Enable the board's BUS drivers
188717d4247SJustin T. Gibbs 	 */
189717d4247SJustin T. Gibbs 	ahc_outb(ahc, BCTL, ENABLE);
190717d4247SJustin T. Gibbs 
191717d4247SJustin T. Gibbs 	return (0);
192717d4247SJustin T. Gibbs }
193717d4247SJustin T. Gibbs 
194717d4247SJustin T. Gibbs /*
195717d4247SJustin T. Gibbs  * Read the 284x SEEPROM.
196717d4247SJustin T. Gibbs  */
197717d4247SJustin T. Gibbs static void
198717d4247SJustin T. Gibbs aha2840_load_seeprom(struct ahc_softc *ahc)
199717d4247SJustin T. Gibbs {
200717d4247SJustin T. Gibbs 	struct	  seeprom_descriptor sd;
201717d4247SJustin T. Gibbs 	struct	  seeprom_config sc;
202717d4247SJustin T. Gibbs 	uint16_t  checksum = 0;
203717d4247SJustin T. Gibbs 	uint8_t   scsi_conf;
204717d4247SJustin T. Gibbs 	int	  have_seeprom;
205717d4247SJustin T. Gibbs 
206717d4247SJustin T. Gibbs 	sd.sd_ahc = ahc;
207717d4247SJustin T. Gibbs 	sd.sd_control_offset = SEECTL_2840;
208717d4247SJustin T. Gibbs 	sd.sd_status_offset = STATUS_2840;
209717d4247SJustin T. Gibbs 	sd.sd_dataout_offset = STATUS_2840;
210717d4247SJustin T. Gibbs 	sd.sd_chip = C46;
211717d4247SJustin T. Gibbs 	sd.sd_MS = 0;
212717d4247SJustin T. Gibbs 	sd.sd_RDY = EEPROM_TF;
213717d4247SJustin T. Gibbs 	sd.sd_CS = CS_2840;
214717d4247SJustin T. Gibbs 	sd.sd_CK = CK_2840;
215717d4247SJustin T. Gibbs 	sd.sd_DO = DO_2840;
216717d4247SJustin T. Gibbs 	sd.sd_DI = DI_2840;
217717d4247SJustin T. Gibbs 
218717d4247SJustin T. Gibbs 	if (bootverbose)
219717d4247SJustin T. Gibbs 		printf("%s: Reading SEEPROM...", ahc_name(ahc));
220717d4247SJustin T. Gibbs 	have_seeprom = read_seeprom(&sd,
221717d4247SJustin T. Gibbs 				    (uint16_t *)&sc,
222717d4247SJustin T. Gibbs 				    /*start_addr*/0,
223717d4247SJustin T. Gibbs 				    sizeof(sc)/2);
224717d4247SJustin T. Gibbs 
225717d4247SJustin T. Gibbs 	if (have_seeprom) {
226717d4247SJustin T. Gibbs 		/* Check checksum */
227717d4247SJustin T. Gibbs 		int i;
228717d4247SJustin T. Gibbs 		int maxaddr = (sizeof(sc)/2) - 1;
229717d4247SJustin T. Gibbs 		uint16_t *scarray = (uint16_t *)&sc;
230717d4247SJustin T. Gibbs 
231717d4247SJustin T. Gibbs 		for (i = 0; i < maxaddr; i++)
232717d4247SJustin T. Gibbs 			checksum = checksum + scarray[i];
233717d4247SJustin T. Gibbs 		if (checksum != sc.checksum) {
234717d4247SJustin T. Gibbs 			if(bootverbose)
235717d4247SJustin T. Gibbs 				printf ("checksum error\n");
236717d4247SJustin T. Gibbs 			have_seeprom = 0;
237717d4247SJustin T. Gibbs 		} else if (bootverbose) {
238717d4247SJustin T. Gibbs 			printf("done.\n");
239717d4247SJustin T. Gibbs 		}
240717d4247SJustin T. Gibbs 	}
241717d4247SJustin T. Gibbs 
242717d4247SJustin T. Gibbs 	if (!have_seeprom) {
243717d4247SJustin T. Gibbs 		if (bootverbose)
244717d4247SJustin T. Gibbs 			printf("%s: No SEEPROM available\n", ahc_name(ahc));
245717d4247SJustin T. Gibbs 		ahc->flags |= AHC_USEDEFAULTS;
246717d4247SJustin T. Gibbs 	} else {
247717d4247SJustin T. Gibbs 		/*
248717d4247SJustin T. Gibbs 		 * Put the data we've collected down into SRAM
249717d4247SJustin T. Gibbs 		 * where ahc_init will find it.
250717d4247SJustin T. Gibbs 		 */
251717d4247SJustin T. Gibbs 		int i;
252717d4247SJustin T. Gibbs 		int max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8;
253717d4247SJustin T. Gibbs 		uint16_t discenable;
254717d4247SJustin T. Gibbs 
255717d4247SJustin T. Gibbs 		discenable = 0;
256717d4247SJustin T. Gibbs 		for (i = 0; i < max_targ; i++){
257717d4247SJustin T. Gibbs 	                uint8_t target_settings;
258717d4247SJustin T. Gibbs 			target_settings = (sc.device_flags[i] & CFXFER) << 4;
259717d4247SJustin T. Gibbs 			if (sc.device_flags[i] & CFSYNCH)
260717d4247SJustin T. Gibbs 				target_settings |= SOFS;
261717d4247SJustin T. Gibbs 			if (sc.device_flags[i] & CFWIDEB)
262717d4247SJustin T. Gibbs 				target_settings |= WIDEXFER;
263717d4247SJustin T. Gibbs 			if (sc.device_flags[i] & CFDISC)
264717d4247SJustin T. Gibbs 				discenable |= (0x01 << i);
265717d4247SJustin T. Gibbs 			ahc_outb(ahc, TARG_SCSIRATE + i, target_settings);
266717d4247SJustin T. Gibbs 		}
267717d4247SJustin T. Gibbs 		ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
268717d4247SJustin T. Gibbs 		ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
269717d4247SJustin T. Gibbs 
270717d4247SJustin T. Gibbs 		ahc->our_id = sc.brtime_id & CFSCSIID;
271717d4247SJustin T. Gibbs 
272717d4247SJustin T. Gibbs 		scsi_conf = (ahc->our_id & 0x7);
273717d4247SJustin T. Gibbs 		if (sc.adapter_control & CFSPARITY)
274717d4247SJustin T. Gibbs 			scsi_conf |= ENSPCHK;
275717d4247SJustin T. Gibbs 		if (sc.adapter_control & CFRESETB)
276717d4247SJustin T. Gibbs 			scsi_conf |= RESET_SCSI;
277717d4247SJustin T. Gibbs 
278717d4247SJustin T. Gibbs 		if (sc.bios_control & CF284XEXTEND)
279717d4247SJustin T. Gibbs 			ahc->flags |= AHC_EXTENDED_TRANS_A;
280717d4247SJustin T. Gibbs 		/* Set SCSICONF info */
281717d4247SJustin T. Gibbs 		ahc_outb(ahc, SCSICONF, scsi_conf);
282717d4247SJustin T. Gibbs 
283717d4247SJustin T. Gibbs 		if (sc.adapter_control & CF284XSTERM)
284717d4247SJustin T. Gibbs 			ahc->flags |= AHC_TERM_ENB_A;
285717d4247SJustin T. Gibbs 	}
286717d4247SJustin T. Gibbs }
287717d4247SJustin T. Gibbs 
288717d4247SJustin T. Gibbs static int
289717d4247SJustin T. Gibbs ahc_aic7770_VL_setup(ahc_dev_softc_t dev, struct ahc_probe_config *probe_config)
290717d4247SJustin T. Gibbs {
291717d4247SJustin T. Gibbs 	int error;
292717d4247SJustin T. Gibbs 
293717d4247SJustin T. Gibbs 	error = ahc_aic7770_setup(dev, probe_config);
294717d4247SJustin T. Gibbs 	probe_config->chip |= AHC_VL;
295717d4247SJustin T. Gibbs 	return (error);
296717d4247SJustin T. Gibbs }
297717d4247SJustin T. Gibbs 
298717d4247SJustin T. Gibbs static int
299717d4247SJustin T. Gibbs ahc_aic7770_EISA_setup(ahc_dev_softc_t dev,
300717d4247SJustin T. Gibbs 		       struct ahc_probe_config *probe_config)
301717d4247SJustin T. Gibbs {
302717d4247SJustin T. Gibbs 	int error;
303717d4247SJustin T. Gibbs 
304717d4247SJustin T. Gibbs 	error = ahc_aic7770_setup(dev, probe_config);
305717d4247SJustin T. Gibbs 	probe_config->chip |= AHC_EISA;
306717d4247SJustin T. Gibbs 	return (error);
307717d4247SJustin T. Gibbs }
308717d4247SJustin T. Gibbs 
309717d4247SJustin T. Gibbs static int
310717d4247SJustin T. Gibbs ahc_aic7770_setup(ahc_dev_softc_t dev, struct ahc_probe_config *probe_config)
311717d4247SJustin T. Gibbs {
312717d4247SJustin T. Gibbs 	probe_config->channel = 'A';
313717d4247SJustin T. Gibbs 	probe_config->channel_b = 'B';
314717d4247SJustin T. Gibbs 	probe_config->chip = AHC_AIC7770;
315717d4247SJustin T. Gibbs 	probe_config->features = AHC_AIC7770_FE;
316717d4247SJustin T. Gibbs 	probe_config->bugs |= AHC_TMODE_WIDEODD_BUG;
317717d4247SJustin T. Gibbs 	probe_config->flags |= AHC_PAGESCBS;
318717d4247SJustin T. Gibbs 	return (0);
319717d4247SJustin T. Gibbs }
320