aha_isa.c revision 122385
1/*
2 * Product specific probe and attach routines for:
3 *      Adaptec 154x.
4 *
5 * Copyright (c) 1999-2003 M. Warner Losh
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions, and the following disclaimer,
13 *    without modification, immediately at the beginning of the file.
14 * 2. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * Derived from bt isa from end, written by:
30 *
31 * Copyright (c) 1998 Justin T. Gibbs
32 * All rights reserved.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 * 1. Redistributions of source code must retain the above copyright
38 *    notice, this list of conditions, and the following disclaimer,
39 *    without modification, immediately at the beginning of the file.
40 * 2. The name of the author may not be used to endorse or promote products
41 *    derived from this software without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
47 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 */
55
56#include <sys/cdefs.h>
57__FBSDID("$FreeBSD: head/sys/dev/aha/aha_isa.c 122385 2003-11-10 02:47:11Z imp $");
58
59#include <sys/param.h>
60#include <sys/systm.h>
61#include <sys/kernel.h>
62#include <sys/lock.h>
63#include <sys/mutex.h>
64
65#include <machine/bus_pio.h>
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
78static 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 */
90static 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 */
103static int
104aha_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	struct resource *port_res;
113	int	port_rid;
114	int	drq;
115	int	irq;
116	config_data_t config_data;
117
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	port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid,
124	  0, ~0, AHA_NREGS, RF_ACTIVE);
125
126	if (port_res == NULL)
127		return (ENXIO);
128
129	port_start = rman_get_start(port_res);
130	aha_alloc(aha, device_get_unit(dev), rman_get_bustag(port_res),
131	    rman_get_bushandle(port_res));
132
133	/* See if there is really a card present */
134	if (aha_probe(aha) || aha_fetch_adapter_info(aha)) {
135		aha_free(aha);
136		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
137		return (ENXIO);
138	}
139
140	/*
141	 * Determine our IRQ, and DMA settings and
142	 * export them to the configuration system.
143	 */
144	error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0,
145	    (uint8_t*)&config_data, sizeof(config_data), DEFAULT_CMD_TIMEOUT);
146
147	if (error != 0) {
148		device_printf(dev, "Could not determine IRQ or DMA "
149		    "settings for adapter at %#jx.  Failing probe\n",
150		    (uintmax_t)port_start);
151		aha_free(aha);
152		bus_release_resource(dev, SYS_RES_IOPORT, port_rid,
153		    port_res);
154		return (ENXIO);
155	}
156
157	bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
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 */
186static int
187aha_isa_attach(device_t dev)
188{
189	struct	aha_softc *aha = device_get_softc(dev);
190	bus_dma_filter_t *filter;
191	void		 *filter_arg;
192	bus_addr_t	 lowaddr;
193	void		 *ih;
194	int		 error;
195
196	aha->portrid = 0;
197	aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &aha->portrid,
198	    0, ~0, AHA_NREGS, RF_ACTIVE);
199	if (!aha->port) {
200		device_printf(dev, "Unable to allocate I/O ports\n");
201		return ENOMEM;
202	}
203
204	aha->irqrid = 0;
205	aha->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &aha->irqrid, 0, ~0, 1,
206	    RF_ACTIVE);
207	if (!aha->irq) {
208		device_printf(dev, "Unable to allocate excluse use of irq\n");
209		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
210		return ENOMEM;
211	}
212
213	aha->drqrid = 0;
214	aha->drq = bus_alloc_resource(dev, SYS_RES_DRQ, &aha->drqrid, 0, ~0, 1,
215	    RF_ACTIVE);
216	if (!aha->drq) {
217		device_printf(dev, "Unable to allocate drq\n");
218		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
219		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
220		return ENOMEM;
221	}
222
223#if 0				/* is the drq ever unset? */
224	if (dev->id_drq != -1)
225		isa_dmacascade(dev->id_drq);
226#endif
227	isa_dmacascade(rman_get_start(aha->drq));
228
229	/* Allocate our parent dmatag */
230	filter = NULL;
231	filter_arg = NULL;
232	lowaddr = BUS_SPACE_MAXADDR_24BIT;
233
234	if (bus_dma_tag_create(	/* parent	*/ NULL,
235				/* alignemnt	*/ 1,
236				/* boundary	*/ 0,
237				/* lowaddr	*/ lowaddr,
238				/* highaddr	*/ BUS_SPACE_MAXADDR,
239				/* filter	*/ filter,
240				/* filterarg	*/ filter_arg,
241				/* maxsize	*/ BUS_SPACE_MAXSIZE_24BIT,
242				/* nsegments	*/ ~0,
243				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_24BIT,
244				/* flags	*/ 0,
245				/* lockfunc	*/ busdma_lock_mutex,
246				/* lockarg	*/ &Giant,
247				&aha->parent_dmat) != 0) {
248                aha_free(aha);
249		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
250		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
251		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
252                return (ENOMEM);
253        }
254
255        if (aha_init(aha)) {
256		device_printf(dev, "init failed\n");
257                aha_free(aha);
258		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
259		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
260		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
261                return (ENOMEM);
262        }
263
264	error = aha_attach(aha);
265	if (error) {
266		device_printf(dev, "attach failed\n");
267                aha_free(aha);
268		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
269		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
270		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
271                return (error);
272	}
273
274	error = bus_setup_intr(dev, aha->irq, INTR_TYPE_CAM|INTR_ENTROPY, aha_intr, aha,
275	    &ih);
276	if (error) {
277		device_printf(dev, "Unable to register interrupt handler\n");
278                aha_free(aha);
279		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
280		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
281		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
282                return (error);
283	}
284
285	return (0);
286}
287
288static int
289aha_isa_detach(device_t dev)
290{
291	struct aha_softc *aha = (struct aha_softc *)device_get_softc(dev);
292	int error;
293
294	error = bus_teardown_intr(dev, aha->irq, aha->ih);
295	if (error) {
296		device_printf(dev, "failed to unregister interrupt handler\n");
297	}
298
299	bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
300	bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
301	bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
302
303	error = aha_detach(aha);
304	if (error) {
305		device_printf(dev, "detach failed\n");
306		return (error);
307	}
308	aha_free(aha);
309
310	return (0);
311}
312
313static void
314aha_isa_identify(driver_t *driver, device_t parent)
315{
316	int i;
317	bus_addr_t ioport;
318	struct aha_softc aha;
319	int rid;
320	struct resource *res;
321	device_t child;
322
323	/* Attempt to find an adapter */
324	for (i = 0; i < sizeof(aha_board_ports) / sizeof(aha_board_ports[0]);
325	    i++) {
326		bzero(&aha, sizeof(aha));
327		ioport = aha_board_ports[i];
328		/*
329		 * XXX Check to see if we have a hard-wired aha device at
330		 * XXX this port, if so, skip.  This should also cover the
331		 * XXX case where we are run multiple times due to, eg,
332		 * XXX kldload/kldunload.
333		 */
334		rid = 0;
335		res = bus_alloc_resource(parent, SYS_RES_IOPORT, &rid,
336		    ioport, ioport, AHA_NREGS, RF_ACTIVE);
337		if (res == NULL)
338			continue;
339		aha_alloc(&aha, -1, rman_get_bustag(res),
340		    rman_get_bushandle(res));
341		/* See if there is really a card present */
342		if (aha_probe(&aha) || aha_fetch_adapter_info(&aha))
343			goto not_this_one;
344		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "aha", -1);
345		bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, AHA_NREGS);
346		/*
347		 * Could query the board and set IRQ/DRQ, but probe does
348		 * that.
349		 */
350	not_this_one:;
351		bus_release_resource(parent, SYS_RES_IOPORT, rid, res);
352		aha_free(&aha);
353	}
354}
355
356static device_method_t aha_isa_methods[] = {
357	/* Device interface */
358	DEVMETHOD(device_probe,		aha_isa_probe),
359	DEVMETHOD(device_attach,	aha_isa_attach),
360	DEVMETHOD(device_detach,	aha_isa_detach),
361	DEVMETHOD(device_identify,	aha_isa_identify),
362
363	{ 0, 0 }
364};
365
366static driver_t aha_isa_driver = {
367	"aha",
368	aha_isa_methods,
369	sizeof(struct aha_softc),
370};
371
372static devclass_t aha_devclass;
373
374DRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, 0, 0);
375