xref: /openbsd/sys/dev/eisa/ahc_eisa.c (revision 471aeecf)
1 /*	$OpenBSD: ahc_eisa.c,v 1.25 2022/04/06 18:59:28 naddy 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.25 2022/04/06 18:59:28 naddy 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 const 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
ahc_eisa_irq(bus_space_tag_t iot,bus_space_handle_t ioh)70 ahc_eisa_irq(bus_space_tag_t iot, bus_space_handle_t ioh)
71 {
72 	int irq;
73 	u_char intdef;
74 	u_char hcntrl;
75 
76 	/* Pause the card preserving the IRQ type */
77 	hcntrl = bus_space_read_1(iot, ioh, HCNTRL) & IRQMS;
78 	bus_space_write_1(iot, ioh, HCNTRL, hcntrl | PAUSE);
79 
80 	intdef = bus_space_read_1(iot, ioh, INTDEF);
81 	switch (irq = (intdef & VECTOR)) {
82 	case 9:
83 	case 10:
84 	case 11:
85 	case 12:
86 	case 14:
87 	case 15:
88 		break;
89 	default:
90 		printf("ahc_eisa_irq: illegal irq setting %d\n", intdef);
91 		return -1;
92 	}
93 
94 	/* Note that we are going and return (to probe) */
95 	return irq;
96 }
97 
98 /*
99  * Check the slots looking for a board we recognise
100  * If we find one, note its address (slot) and call
101  * the actual probe routine to check it out.
102  */
103 int
ahc_eisa_match(struct device * parent,void * match,void * aux)104 ahc_eisa_match(struct device *parent, void *match, void *aux)
105 {
106 	struct eisa_attach_args *ea = aux;
107 	bus_space_tag_t iot = ea->ea_iot;
108 	bus_space_handle_t ioh;
109 	int irq;
110 
111 	/* must match one of our known ID strings */
112 	if (strcmp(ea->ea_idstring, "ADP7770") &&
113 		 strcmp(ea->ea_idstring, "ADP7771")
114 #if 0
115 		 && strcmp(ea->ea_idstring, "ADP7756")	/* not EISA, but VL */
116 		 && strcmp(ea->ea_idstring, "ADP7757")	/* not EISA, but VL */
117 #endif
118 		)
119 		return (0);
120 
121 	if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) +
122 			  AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh))
123 		return (0);
124 
125 	irq = ahc_eisa_irq(iot, ioh);
126 
127 	bus_space_unmap(iot, ioh, AHC_EISA_IOSIZE);
128 
129 	return (irq >= 0);
130 }
131 
132 void
ahc_eisa_attach(struct device * parent,struct device * self,void * aux)133 ahc_eisa_attach(struct device *parent, struct device *self, void *aux)
134 {
135 	struct ahc_softc *ahc = (void *)self;
136 	struct eisa_attach_args *ea = aux;
137 	bus_space_tag_t iot = ea->ea_iot;
138 	bus_space_handle_t ioh;
139 	int irq;
140 	eisa_chipset_tag_t ec = ea->ea_ec;
141 	eisa_intr_handle_t ih;
142 	const char *model, *intrstr;
143 	u_int biosctrl;
144 	u_int scsiconf;
145 	u_int scsiconf1;
146 	u_int intdef;
147 	int i;
148 
149 	ahc_set_name(ahc, ahc->sc_dev.dv_xname);
150 	ahc_set_unit(ahc, ahc->sc_dev.dv_unit);
151 
152 	/* set dma tags */
153 	ahc->parent_dmat = ea->ea_dmat;
154 
155 	if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) +
156 			  AHC_EISA_SLOT_OFFSET, AHC_EISA_IOSIZE, 0, &ioh))
157 		panic("ahc_eisa_attach: can't map i/o addresses");
158 	if ((irq = ahc_eisa_irq(iot, ioh)) < 0)
159 		panic("ahc_eisa_attach: ahc_eisa_irq failed!");
160 
161 	if (strcmp(ea->ea_idstring, "ADP7770") == 0) {
162 		model = EISA_PRODUCT_ADP7770;
163 	} else if (strcmp(ea->ea_idstring, "ADP7771") == 0) {
164 		model = EISA_PRODUCT_ADP7771;
165 	} else {
166 		panic("ahc_eisa_attach: Unknown device type %s",
167 				ea->ea_idstring);
168 	}
169 	printf(": %s\n", model);
170 
171 	/*
172 	 * Instead of ahc_alloc() as in FreeBSD, do the few relevant
173 	 * initializations manually.
174 	 */
175 	LIST_INIT(&ahc->pending_scbs);
176 	for (i = 0; i < AHC_NUM_TARGETS; i++)
177 		TAILQ_INIT(&ahc->untagged_queues[i]);
178 
179 	/*
180 	 * SCSI_IS_SCSIBUS_B() must returns false until sc_channel_b
181 	 * has been properly initialized.
182 	 */
183 	ahc->sc_child_b = NULL;
184 
185 	ahc->channel = 'A';
186 	ahc->chip = AHC_AIC7770|AHC_EISA;
187 	ahc->features = AHC_AIC7770_FE;
188 	ahc->bugs |= AHC_TMODE_WIDEODD_BUG;
189 	ahc->flags |= AHC_PAGESCBS;
190 	ahc->tag = iot;
191 	ahc->bsh = ioh;
192 	ahc->bus_chip_init = ahc_chip_init;
193 	ahc->instruction_ram_size = 512;
194 
195 	if (ahc_softc_init(ahc) != 0)
196 		return;
197 
198 	if (ahc_reset(ahc, /*reinit*/FALSE) != 0)
199 		return;
200 
201 	/* See if we are edge triggered */
202 	intdef = ahc_inb(ahc, INTDEF);
203 	if ((intdef & EDGE_TRIG) != 0)
204 		ahc->flags |= AHC_EDGE_INTERRUPT;
205 
206 	if (eisa_intr_map(ec, irq, &ih)) {
207 		printf("%s: couldn't map interrupt (%d)\n",
208 		       ahc->sc_dev.dv_xname, irq);
209 		return;
210 	}
211 
212 	/*
213 	 * Tell the user what type of interrupts we're using.
214 	 * useful for debugging irq problems
215 	 */
216 	if (bootverbose) {
217 		printf("%s: Using %s Interrupts\n",
218 		       ahc_name(ahc),
219 		       ahc->pause & IRQMS ?
220 		       "Level Sensitive" : "Edge Triggered");
221 	}
222 
223 	/*
224 	 * Now that we know we own the resources we need, do the
225 	 * card initialization.
226 	 *
227 	 * First, the aic7770 card specific setup.
228 	 */
229 	biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
230 	scsiconf = ahc_inb(ahc, SCSICONF);
231 	scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
232 
233 	/* Get the primary channel information */
234 	if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
235 		ahc->flags |= AHC_PRIMARY_CHANNEL;
236 
237 	if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
238 		ahc->flags |= AHC_USEDEFAULTS;
239 	} else if ((ahc->features & AHC_WIDE) != 0) {
240 		ahc->our_id = scsiconf1 & HWSCSIID;
241 		if (scsiconf & TERM_ENB)
242 			ahc->flags |= AHC_TERM_ENB_A;
243 	} else {
244 		ahc->our_id = scsiconf & HSCSIID;
245 		ahc->our_id_b = scsiconf1 & HSCSIID;
246 		if (scsiconf & TERM_ENB)
247 			ahc->flags |= AHC_TERM_ENB_A;
248 		if (scsiconf1 & TERM_ENB)
249 			ahc->flags |= AHC_TERM_ENB_B;
250 	}
251 	/*
252 	 * We have no way to tell, so assume extended
253 	 * translation is enabled.
254 	 */
255 
256 	ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
257 
258 	/*
259 	 * See if we have a Rev E or higher aic7770. Anything below a
260 	 * Rev E will have a R/O autoflush disable configuration bit.
261 	 * It's still not clear exactly what is different about the Rev E.
262 	 * We think it allows 8 bit entries in the QOUTFIFO to support
263 	 * "paging" SCBs so you can have more than 4 commands active at
264 	 * once.
265 	 */
266 	{
267 		char *id_string;
268 		u_char sblkctl;
269 		u_char sblkctl_orig;
270 
271 		sblkctl_orig = ahc_inb(ahc, SBLKCTL);
272 		sblkctl = sblkctl_orig ^ AUTOFLUSHDIS;
273 		ahc_outb(ahc, SBLKCTL, sblkctl);
274 		sblkctl = ahc_inb(ahc, SBLKCTL);
275 		if (sblkctl != sblkctl_orig) {
276 			id_string = "aic7770 >= Rev E";
277 			/*
278 			 * Ensure autoflush is enabled
279 			 */
280 			sblkctl &= ~AUTOFLUSHDIS;
281 			ahc_outb(ahc, SBLKCTL, sblkctl);
282 
283 			/* Allow paging on this adapter */
284 			ahc->flags |= AHC_PAGESCBS;
285 		} else
286 			id_string = "aic7770 <= Rev C";
287 
288 		if (bootverbose)
289 			printf("%s: %s\n", ahc_name(ahc), id_string);
290 	}
291 
292 	/* Setup the FIFO threshold and the bus off time */
293 	{
294 		u_char hostconf = ahc_inb(ahc, HOSTCONF);
295 		ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
296 		ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
297 	}
298 
299 	/*
300 	 * Generic aic7xxx initialization.
301 	 */
302 	if (ahc_init(ahc)) {
303 		ahc_free(ahc);
304 		return;
305 	}
306 
307 	/*
308 	 * Link this softc in with all other ahc instances.
309 	 */
310 	ahc_softc_insert(ahc);
311 
312 	/*
313 	 * Enable the board's BUS drivers
314 	 */
315 	ahc_outb(ahc, BCTL, ENABLE);
316 
317 	intrstr = eisa_intr_string(ec, ih);
318 	/*
319 	 * The IRQMS bit enables level sensitive interrupts only allow
320 	 * IRQ sharing if its set.
321 	 */
322 	ahc->ih = eisa_intr_establish(ec, ih,
323 	    ahc->pause & IRQMS ? IST_LEVEL : IST_EDGE, IPL_BIO,
324 	    ahc_platform_intr, ahc, ahc->sc_dev.dv_xname);
325 	if (ahc->ih == NULL) {
326 		printf("%s: couldn't establish interrupt",
327 		       ahc->sc_dev.dv_xname);
328 		if (intrstr != NULL)
329 			printf(" at %s", intrstr);
330 		printf("\n");
331 		ahc_free(ahc);
332 		return;
333 	}
334 	if (intrstr != NULL)
335 		printf("%s: interrupting at %s\n", ahc->sc_dev.dv_xname,
336 		    intrstr);
337 
338 	ahc_intr_enable(ahc, TRUE);
339 
340 	/* Attach sub-devices - always succeeds */
341 	ahc_attach(ahc);
342 
343 }
344