xref: /openbsd/sys/dev/eisa/ahc_eisa.c (revision a6445c1d)
1 /*	$OpenBSD: ahc_eisa.c,v 1.21 2012/05/12 21:54:39 miod Exp $	*/
2 /*	$NetBSD: ahc_eisa.c,v 1.10 1996/10/21 22:30:58 thorpej Exp $	*/
3 
4 /*
5  * Product specific probe and attach routines for:
6  * 	27/284X and aic7770 motherboard SCSI controllers
7  *
8  * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice immediately at the beginning of the file, without modification,
16  *    this list of conditions, and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. 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  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
27  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	$Id: ahc_eisa.c,v 1.21 2012/05/12 21:54:39 miod Exp $
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/device.h>
42 #include <machine/bus.h>
43 #include <machine/intr.h>
44 
45 #include <scsi/scsi_all.h>
46 #include <scsi/scsiconf.h>
47 
48 #include <dev/eisa/eisareg.h>
49 #include <dev/eisa/eisavar.h>
50 #include <dev/eisa/eisadevs.h>
51 #include <dev/ic/aic7xxx_openbsd.h>
52 #include <dev/ic/aic7xxx_inline.h>
53 
54 #define AHC_EISA_SLOT_OFFSET	0xc00
55 #define AHC_EISA_IOSIZE		0x100
56 
57 int   ahc_eisa_irq(bus_space_tag_t, bus_space_handle_t);
58 int   ahc_eisa_match(struct device *, void *, void *);
59 void  ahc_eisa_attach(struct device *, struct device *, void *);
60 
61 
62 struct cfattach ahc_eisa_ca = {
63 	sizeof(struct ahc_softc), ahc_eisa_match, ahc_eisa_attach
64 };
65 
66 /*
67  * Return irq setting of the board, otherwise -1.
68  */
69 int
70 ahc_eisa_irq(iot, ioh)
71 bus_space_tag_t iot;
72 bus_space_handle_t ioh;
73 {
74 	int irq;
75 	u_char intdef;
76 	u_char hcntrl;
77 
78 	/* Pause the card preserving the IRQ type */
79 	hcntrl = bus_space_read_1(iot, ioh, HCNTRL) & IRQMS;
80 	bus_space_write_1(iot, ioh, HCNTRL, hcntrl | PAUSE);
81 
82 	intdef = bus_space_read_1(iot, ioh, INTDEF);
83 	switch (irq = (intdef & VECTOR)) {
84 	case 9:
85 	case 10:
86 	case 11:
87 	case 12:
88 	case 14:
89 	case 15:
90 		break;
91 	default:
92 		printf("ahc_eisa_irq: illegal irq setting %d\n", intdef);
93 		return -1;
94 	}
95 
96 	/* Note that we are going and return (to probe) */
97 	return irq;
98 }
99 
100 /*
101  * Check the slots looking for a board we recognise
102  * If we find one, note its address (slot) and call
103  * the actual probe routine to check it out.
104  */
105 int
106 ahc_eisa_match(parent, match, aux)
107 struct device *parent;
108 void *match, *aux;
109 {
110 	struct eisa_attach_args *ea = aux;
111 	bus_space_tag_t iot = ea->ea_iot;
112 	bus_space_handle_t ioh;
113 	int irq;
114 
115 	/* must match one of our known ID strings */
116 	if (strcmp(ea->ea_idstring, "ADP7770") &&
117 		 strcmp(ea->ea_idstring, "ADP7771")
118 #if 0
119 		 && strcmp(ea->ea_idstring, "ADP7756")	/* not EISA, but VL */
120 		 && strcmp(ea->ea_idstring, "ADP7757")	/* not EISA, but VL */
121 #endif
122 		)
123 		return (0);
124 
125 	if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) +
126 			  AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh))
127 		return (0);
128 
129 	irq = ahc_eisa_irq(iot, ioh);
130 
131 	bus_space_unmap(iot, ioh, AHC_EISA_IOSIZE);
132 
133 	return (irq >= 0);
134 }
135 
136 void
137 ahc_eisa_attach(parent, self, aux)
138 struct device *parent, *self;
139 void *aux;
140 {
141 	struct ahc_softc *ahc = (void *)self;
142 	struct eisa_attach_args *ea = aux;
143 	bus_space_tag_t iot = ea->ea_iot;
144 	bus_space_handle_t ioh;
145 	int irq;
146 	eisa_chipset_tag_t ec = ea->ea_ec;
147 	eisa_intr_handle_t ih;
148 	const char *model, *intrstr;
149 	u_int biosctrl;
150 	u_int scsiconf;
151 	u_int scsiconf1;
152 	u_int intdef;
153 	int i;
154 
155 	ahc_set_name(ahc, ahc->sc_dev.dv_xname);
156 	ahc_set_unit(ahc, ahc->sc_dev.dv_unit);
157 
158 	/* set dma tags */
159 	ahc->parent_dmat = ea->ea_dmat;
160 
161 	if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) +
162 			  AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh))
163 		panic("ahc_eisa_attach: can't map i/o addresses");
164 	if ((irq = ahc_eisa_irq(iot, ioh)) < 0)
165 		panic("ahc_eisa_attach: ahc_eisa_irq failed!");
166 
167 	if (strcmp(ea->ea_idstring, "ADP7770") == 0) {
168 		model = EISA_PRODUCT_ADP7770;
169 	} else if (strcmp(ea->ea_idstring, "ADP7771") == 0) {
170 		model = EISA_PRODUCT_ADP7771;
171 	} else {
172 		panic("ahc_eisa_attach: Unknown device type %s",
173 				ea->ea_idstring);
174 	}
175 	printf(": %s\n", model);
176 
177 	/*
178 	 * Instead of ahc_alloc() as in FreeBSD, do the few relevant
179 	 * initializations manually.
180 	 */
181 	LIST_INIT(&ahc->pending_scbs);
182 	for (i = 0; i < AHC_NUM_TARGETS; i++)
183 		TAILQ_INIT(&ahc->untagged_queues[i]);
184 
185 	/*
186 	 * SCSI_IS_SCSIBUS_B() must returns false until sc_channel_b
187 	 * has been properly initialized. XXX Breaks if >254 scsi buses.
188 	 */
189 	ahc->sc_channel_b.scsibus = 0xff;
190 
191 	ahc->channel = 'A';
192 	ahc->chip = AHC_AIC7770|AHC_EISA;
193 	ahc->features = AHC_AIC7770_FE;
194 	ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
195 	ahc->flags |= AHC_PAGESCBS;
196 	ahc->tag = iot;
197 	ahc->bsh = ioh;
198 	ahc->bus_chip_init = ahc_chip_init;
199 	ahc->instruction_ram_size = 512;
200 
201 	if (ahc_softc_init(ahc) != 0)
202 		return;
203 
204 	if (ahc_reset(ahc, /*reinit*/FALSE) != 0)
205 		return;
206 
207 	/* See if we are edge triggered */
208 	intdef = ahc_inb(ahc, INTDEF);
209 	if ((intdef & EDGE_TRIG) != 0)
210 		ahc->flags |= AHC_EDGE_INTERRUPT;
211 
212 	if (eisa_intr_map(ec, irq, &ih)) {
213 		printf("%s: couldn't map interrupt (%d)\n",
214 		       ahc->sc_dev.dv_xname, irq);
215 		return;
216 	}
217 
218 	/*
219 	 * Tell the user what type of interrupts we're using.
220 	 * useful for debugging irq problems
221 	 */
222 	if (bootverbose) {
223 		printf("%s: Using %s Interrupts\n",
224 		       ahc_name(ahc),
225 		       ahc->pause & IRQMS ?
226 		       "Level Sensitive" : "Edge Triggered");
227 	}
228 
229 	/*
230 	 * Now that we know we own the resources we need, do the
231 	 * card initialization.
232 	 *
233 	 * First, the aic7770 card specific setup.
234 	 */
235 	biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
236 	scsiconf = ahc_inb(ahc, SCSICONF);
237 	scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
238 
239 	/* Get the primary channel information */
240 	if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
241 		ahc->flags |= AHC_PRIMARY_CHANNEL;
242 
243 	if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
244 		ahc->flags |= AHC_USEDEFAULTS;
245 	} else if ((ahc->features & AHC_WIDE) != 0) {
246 		ahc->our_id = scsiconf1 & HWSCSIID;
247 		if (scsiconf & TERM_ENB)
248 			ahc->flags |= AHC_TERM_ENB_A;
249 	} else {
250 		ahc->our_id = scsiconf & HSCSIID;
251 		ahc->our_id_b = scsiconf1 & HSCSIID;
252 		if (scsiconf & TERM_ENB)
253 			ahc->flags |= AHC_TERM_ENB_A;
254 		if (scsiconf1 & TERM_ENB)
255 			ahc->flags |= AHC_TERM_ENB_B;
256 	}
257 	/*
258 	 * We have no way to tell, so assume extended
259 	 * translation is enabled.
260 	 */
261 
262 	ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
263 
264 	/*
265 	 * See if we have a Rev E or higher aic7770. Anything below a
266 	 * Rev E will have a R/O autoflush disable configuration bit.
267 	 * It's still not clear exactly what is different about the Rev E.
268 	 * We think it allows 8 bit entries in the QOUTFIFO to support
269 	 * "paging" SCBs so you can have more than 4 commands active at
270 	 * once.
271 	 */
272 	{
273 		char *id_string;
274 		u_char sblkctl;
275 		u_char sblkctl_orig;
276 
277 		sblkctl_orig = ahc_inb(ahc, SBLKCTL);
278 		sblkctl = sblkctl_orig ^ AUTOFLUSHDIS;
279 		ahc_outb(ahc, SBLKCTL, sblkctl);
280 		sblkctl = ahc_inb(ahc, SBLKCTL);
281 		if (sblkctl != sblkctl_orig) {
282 			id_string = "aic7770 >= Rev E";
283 			/*
284 			 * Ensure autoflush is enabled
285 			 */
286 			sblkctl &= ~AUTOFLUSHDIS;
287 			ahc_outb(ahc, SBLKCTL, sblkctl);
288 
289 			/* Allow paging on this adapter */
290 			ahc->flags |= AHC_PAGESCBS;
291 		} else
292 			id_string = "aic7770 <= Rev C";
293 
294 		if (bootverbose)
295 			printf("%s: %s\n", ahc_name(ahc), id_string);
296 	}
297 
298 	/* Setup the FIFO threshold and the bus off time */
299 	{
300 		u_char hostconf = ahc_inb(ahc, HOSTCONF);
301 		ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
302 		ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
303 	}
304 
305 	/*
306 	 * Generic aic7xxx initialization.
307 	 */
308 	if (ahc_init(ahc)) {
309 		ahc_free(ahc);
310 		return;
311 	}
312 
313 	/*
314 	 * Link this softc in with all other ahc instances.
315 	 */
316 	ahc_softc_insert(ahc);
317 
318 	/*
319 	 * Enable the board's BUS drivers
320 	 */
321 	ahc_outb(ahc, BCTL, ENABLE);
322 
323 	intrstr = eisa_intr_string(ec, ih);
324 	/*
325 	 * The IRQMS bit enables level sensitive interrupts only allow
326 	 * IRQ sharing if its set.
327 	 */
328 	ahc->ih = eisa_intr_establish(ec, ih,
329 	    ahc->pause & IRQMS ? IST_LEVEL : IST_EDGE, IPL_BIO,
330 	    ahc_platform_intr, ahc, ahc->sc_dev.dv_xname);
331 	if (ahc->ih == NULL) {
332 		printf("%s: couldn't establish interrupt",
333 		       ahc->sc_dev.dv_xname);
334 		if (intrstr != NULL)
335 			printf(" at %s", intrstr);
336 		printf("\n");
337 		ahc_free(ahc);
338 		return;
339 	}
340 	if (intrstr != NULL)
341 		printf("%s: interrupting at %s\n", ahc->sc_dev.dv_xname,
342 		    intrstr);
343 
344 	ahc_intr_enable(ahc, TRUE);
345 
346 	/* Attach sub-devices - always succeeds */
347 	ahc_attach(ahc);
348 
349 }
350