xref: /NextBSD/sys/dev/aha/aha_isa.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*
2  * Product specific probe and attach routines for:
3  *      Adaptec 154x.
4  */
5 /*-
6  * Copyright (c) 1999-2003 M. Warner Losh
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions, and the following disclaimer,
14  *    without modification, immediately at the beginning of the file.
15  * 2. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * Derived from bt isa from end, written by:
31  *
32  * Copyright (c) 1998 Justin T. Gibbs
33  * All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions, and the following disclaimer,
40  *    without modification, immediately at the beginning of the file.
41  * 2. The name of the author may not be used to endorse or promote products
42  *    derived from this software without specific prior written permission.
43  *
44  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
48  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54  * SUCH DAMAGE.
55  */
56 
57 #include <sys/cdefs.h>
58 __FBSDID("$FreeBSD$");
59 
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/kernel.h>
63 #include <sys/lock.h>
64 #include <sys/mutex.h>
65 
66 #include <machine/bus.h>
67 #include <machine/resource.h>
68 #include <sys/module.h>
69 #include <sys/bus.h>
70 #include <sys/rman.h>
71 
72 #include <isa/isavar.h>
73 
74 #include <dev/aha/ahareg.h>
75 
76 #include <cam/scsi/scsi_all.h>
77 
78 static struct isa_pnp_id aha_ids[] = {
79 	{ADP0100_PNP,		"Adaptec 1540/1542 ISA SCSI"},	/* ADP0100 */
80 	{AHA1540_PNP,		"Adaptec 1540/aha-1640/aha-1535"},/* ADP1540 */
81 	{AHA1542_PNP,		"Adaptec 1542/aha-1535"},	/* ADP1542 */
82 	{AHA1542_PNPCOMPAT,	"Adaptec 1542 compatible"},	/* PNP00A0 */
83 	{ICU0091_PNP,		"Adaptec AHA-1540/1542 SCSI"},	/* ICU0091 */
84 	{0}
85 };
86 
87 /*
88  * I/O ports listed in the order enumerated by the card for certain op codes.
89  */
90 static bus_addr_t aha_board_ports[] =
91 {
92 	0x330,
93 	0x334,
94 	0x230,
95 	0x234,
96 	0x130,
97 	0x134
98 };
99 
100 /*
101  * Check if the device can be found at the port given
102  */
103 static int
aha_isa_probe(device_t dev)104 aha_isa_probe(device_t dev)
105 {
106 	/*
107 	 * find unit and check we have that many defined
108 	 */
109 	struct	aha_softc *aha = device_get_softc(dev);
110 	int	error;
111 	u_long	port_start;
112 	int	port_rid;
113 	int	drq;
114 	int	irq;
115 	config_data_t config_data;
116 
117 	aha->dev = dev;
118 	/* Check isapnp ids */
119 	if (ISA_PNP_PROBE(device_get_parent(dev), dev, aha_ids) == ENXIO)
120 		return (ENXIO);
121 
122 	port_rid = 0;
123 	aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid,
124 	    0ul, ~0ul, AHA_NREGS, RF_ACTIVE);
125 
126 	if (aha->port == NULL)
127 		return (ENXIO);
128 
129 	port_start = rman_get_start(aha->port);
130 	aha_alloc(aha);
131 
132 	/* See if there is really a card present */
133 	if (aha_probe(aha) || aha_fetch_adapter_info(aha)) {
134 		aha_free(aha);
135 		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, aha->port);
136 		return (ENXIO);
137 	}
138 
139 	/*
140 	 * Determine our IRQ, and DMA settings and
141 	 * export them to the configuration system.
142 	 */
143 	error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0,
144 	    (uint8_t*)&config_data, sizeof(config_data), DEFAULT_CMD_TIMEOUT);
145 
146 	if (error != 0) {
147 		device_printf(dev, "Could not determine IRQ or DMA "
148 		    "settings for adapter at %#jx.  Failing probe\n",
149 		    (uintmax_t)port_start);
150 		aha_free(aha);
151 		bus_release_resource(dev, SYS_RES_IOPORT, port_rid,
152 		    aha->port);
153 		return (ENXIO);
154 	}
155 
156 	bus_release_resource(dev, SYS_RES_IOPORT, port_rid, aha->port);
157 	aha->port = NULL;
158 
159 	switch (config_data.dma_chan) {
160 	case DMA_CHAN_5:
161 		drq = 5;
162 		break;
163 	case DMA_CHAN_6:
164 		drq = 6;
165 		break;
166 	case DMA_CHAN_7:
167 		drq = 7;
168 		break;
169 	default:
170 		device_printf(dev, "Invalid DMA setting for adapter at %#jx.",
171 		    (uintmax_t)port_start);
172 		return (ENXIO);
173 	}
174 	error = bus_set_resource(dev, SYS_RES_DRQ, 0, drq, 1);
175 	if (error)
176 		return error;
177 
178 	irq = ffs(config_data.irq) + 8;
179 	error = bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
180 	return (error);
181 }
182 
183 /*
184  * Attach all the sub-devices we can find
185  */
186 static int
aha_isa_attach(device_t dev)187 aha_isa_attach(device_t dev)
188 {
189 	struct	aha_softc *aha = device_get_softc(dev);
190 	int		 error = ENOMEM;
191 
192 	aha->dev = dev;
193 	aha->portrid = 0;
194 	aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &aha->portrid,
195 	    0ul, ~0ul, AHA_NREGS, RF_ACTIVE);
196 	if (!aha->port) {
197 		device_printf(dev, "Unable to allocate I/O ports\n");
198 		goto fail;
199 	}
200 
201 	aha->irqrid = 0;
202 	aha->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &aha->irqrid,
203 	    RF_ACTIVE);
204 	if (!aha->irq) {
205 		device_printf(dev, "Unable to allocate excluse use of irq\n");
206 		goto fail;
207 	}
208 
209 	aha->drqrid = 0;
210 	aha->drq = bus_alloc_resource_any(dev, SYS_RES_DRQ, &aha->drqrid,
211 	    RF_ACTIVE);
212 	if (!aha->drq) {
213 		device_printf(dev, "Unable to allocate drq\n");
214 		goto fail;
215 	}
216 
217 #if 0				/* is the drq ever unset? */
218 	if (dev->id_drq != -1)
219 		isa_dmacascade(dev->id_drq);
220 #endif
221 	isa_dmacascade(rman_get_start(aha->drq));
222 
223 	/* Allocate our parent dmatag */
224 	if (bus_dma_tag_create(	/* parent	*/ bus_get_dma_tag(dev),
225 				/* alignemnt	*/ 1,
226 				/* boundary	*/ 0,
227 				/* lowaddr	*/ BUS_SPACE_MAXADDR_24BIT,
228 				/* highaddr	*/ BUS_SPACE_MAXADDR,
229 				/* filter	*/ NULL,
230 				/* filterarg	*/ NULL,
231 				/* maxsize	*/ BUS_SPACE_MAXSIZE_24BIT,
232 				/* nsegments	*/ ~0,
233 				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_24BIT,
234 				/* flags	*/ 0,
235 				/* lockfunc	*/ NULL,
236 				/* lockarg	*/ NULL,
237 				&aha->parent_dmat) != 0) {
238 		device_printf(dev, "dma tag create failed.\n");
239 		goto fail;
240         }
241 
242 	if (aha_init(aha)) {
243 		device_printf(dev, "init failed\n");
244 		goto fail;
245         }
246 	/*
247 	 * The 1542A and B look the same.  So we guess based on
248 	 * the firmware revision.  It appears that only rev 0 is on
249 	 * the A cards.
250 	 */
251 	if (aha->boardid <= BOARD_1542 && aha->fw_major == 0) {
252 		device_printf(dev, "154xA may not work\n");
253 		aha->ccb_sg_opcode = INITIATOR_SG_CCB;
254 		aha->ccb_ccb_opcode = INITIATOR_CCB;
255 	}
256 
257 	error = aha_attach(aha);
258 	if (error) {
259 		device_printf(dev, "attach failed\n");
260 		goto fail;
261 	}
262 
263 	error = bus_setup_intr(dev, aha->irq, INTR_TYPE_CAM|INTR_ENTROPY|
264 	    INTR_MPSAFE, NULL, aha_intr, aha, &aha->ih);
265 	if (error) {
266 		device_printf(dev, "Unable to register interrupt handler\n");
267 		aha_detach(aha);
268                 goto fail;
269 	}
270 
271 	return (0);
272 fail: ;
273 	aha_free(aha);
274 	bus_free_resource(dev, SYS_RES_IOPORT, aha->port);
275 	bus_free_resource(dev, SYS_RES_IRQ, aha->irq);
276 	bus_free_resource(dev, SYS_RES_DRQ, aha->drq);
277 	return (error);
278 }
279 
280 static int
aha_isa_detach(device_t dev)281 aha_isa_detach(device_t dev)
282 {
283 	struct aha_softc *aha = (struct aha_softc *)device_get_softc(dev);
284 	int error;
285 
286 	error = bus_teardown_intr(dev, aha->irq, aha->ih);
287 	if (error)
288 		device_printf(dev, "failed to unregister interrupt handler\n");
289 
290 	error = aha_detach(aha);
291 	if (error) {
292 		device_printf(dev, "detach failed\n");
293 		return (error);
294 	}
295 	aha_free(aha);
296 	bus_free_resource(dev, SYS_RES_IOPORT, aha->port);
297 	bus_free_resource(dev, SYS_RES_IRQ, aha->irq);
298 	bus_free_resource(dev, SYS_RES_DRQ, aha->drq);
299 
300 	return (0);
301 }
302 
303 static void
aha_isa_identify(driver_t * driver,device_t parent)304 aha_isa_identify(driver_t *driver, device_t parent)
305 {
306 	int i;
307 	bus_addr_t ioport;
308 	struct aha_softc aha;
309 	int rid;
310 	device_t child;
311 
312 	/* Attempt to find an adapter */
313 	for (i = 0; i < sizeof(aha_board_ports) / sizeof(aha_board_ports[0]);
314 	    i++) {
315 		bzero(&aha, sizeof(aha));
316 		ioport = aha_board_ports[i];
317 		/*
318 		 * XXX Check to see if we have a hard-wired aha device at
319 		 * XXX this port, if so, skip.  This should also cover the
320 		 * XXX case where we are run multiple times due to, eg,
321 		 * XXX kldload/kldunload.
322 		 */
323 		rid = 0;
324 		aha.port = bus_alloc_resource(parent, SYS_RES_IOPORT, &rid,
325 		    ioport, ioport, AHA_NREGS, RF_ACTIVE);
326 		if (aha.port == NULL)
327 			continue;
328 		aha_alloc(&aha);
329 		/* See if there is really a card present */
330 		if (aha_probe(&aha) || aha_fetch_adapter_info(&aha))
331 			goto not_this_one;
332 		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "aha", -1);
333 		bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, AHA_NREGS);
334 		/*
335 		 * Could query the board and set IRQ/DRQ, but probe does
336 		 * that.
337 		 */
338 	not_this_one:
339 		bus_release_resource(parent, SYS_RES_IOPORT, rid, aha.port);
340 		aha_free(&aha);
341 	}
342 }
343 
344 static device_method_t aha_isa_methods[] = {
345 	/* Device interface */
346 	DEVMETHOD(device_probe,		aha_isa_probe),
347 	DEVMETHOD(device_attach,	aha_isa_attach),
348 	DEVMETHOD(device_detach,	aha_isa_detach),
349 	DEVMETHOD(device_identify,	aha_isa_identify),
350 
351 	{ 0, 0 }
352 };
353 
354 static driver_t aha_isa_driver = {
355 	"aha",
356 	aha_isa_methods,
357 	sizeof(struct aha_softc),
358 };
359 
360 static devclass_t aha_devclass;
361 
362 DRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, 0, 0);
363 MODULE_DEPEND(aha, isa, 1, 1, 1);
364