aha_isa.c revision 140039
1214501Srpaulo/*
2214501Srpaulo * Product specific probe and attach routines for:
3214501Srpaulo *      Adaptec 154x.
4214501Srpaulo */
5252726Srpaulo/*-
6252726Srpaulo * Copyright (c) 1999-2003 M. Warner Losh
7214501Srpaulo * All rights reserved.
8214501Srpaulo *
9214501Srpaulo * Redistribution and use in source and binary forms, with or without
10214501Srpaulo * modification, are permitted provided that the following conditions
11214501Srpaulo * are met:
12214501Srpaulo * 1. Redistributions of source code must retain the above copyright
13214501Srpaulo *    notice, this list of conditions, and the following disclaimer,
14214501Srpaulo *    without modification, immediately at the beginning of the file.
15252726Srpaulo * 2. The name of the author may not be used to endorse or promote products
16214501Srpaulo *    derived from this software without specific prior written permission.
17214501Srpaulo *
18214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21214501Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22214501Srpaulo * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28214501Srpaulo * SUCH DAMAGE.
29214501Srpaulo *
30214501Srpaulo * Derived from bt isa from end, written by:
31214501Srpaulo *
32214501Srpaulo * Copyright (c) 1998 Justin T. Gibbs
33214501Srpaulo * All rights reserved.
34214501Srpaulo *
35214501Srpaulo * Redistribution and use in source and binary forms, with or without
36214501Srpaulo * modification, are permitted provided that the following conditions
37214501Srpaulo * are met:
38214501Srpaulo * 1. Redistributions of source code must retain the above copyright
39214501Srpaulo *    notice, this list of conditions, and the following disclaimer,
40214501Srpaulo *    without modification, immediately at the beginning of the file.
41214501Srpaulo * 2. The name of the author may not be used to endorse or promote products
42214501Srpaulo *    derived from this software without specific prior written permission.
43214501Srpaulo *
44214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47214501Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
48214501Srpaulo * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54214501Srpaulo * SUCH DAMAGE.
55214501Srpaulo */
56214501Srpaulo
57214501Srpaulo#include <sys/cdefs.h>
58214501Srpaulo__FBSDID("$FreeBSD: head/sys/dev/aha/aha_isa.c 140039 2005-01-11 06:22:48Z imp $");
59214501Srpaulo
60214501Srpaulo#include <sys/param.h>
61214501Srpaulo#include <sys/systm.h>
62214501Srpaulo#include <sys/kernel.h>
63214501Srpaulo#include <sys/lock.h>
64214501Srpaulo#include <sys/mutex.h>
65214501Srpaulo
66214501Srpaulo#include <machine/bus_pio.h>
67214501Srpaulo#include <machine/bus.h>
68214501Srpaulo#include <machine/resource.h>
69214501Srpaulo#include <sys/module.h>
70214501Srpaulo#include <sys/bus.h>
71214501Srpaulo#include <sys/rman.h>
72214501Srpaulo
73214501Srpaulo#include <isa/isavar.h>
74214501Srpaulo
75214501Srpaulo#include <dev/aha/ahareg.h>
76214501Srpaulo
77214501Srpaulo#include <cam/scsi/scsi_all.h>
78214501Srpaulo
79214501Srpaulostatic struct isa_pnp_id aha_ids[] = {
80214501Srpaulo	{ADP0100_PNP,		"Adaptec 1540/1542 ISA SCSI"},	/* ADP0100 */
81214501Srpaulo	{AHA1540_PNP,		"Adaptec 1540/aha-1640/aha-1535"},/* ADP1540 */
82214501Srpaulo	{AHA1542_PNP,		"Adaptec 1542/aha-1535"},	/* ADP1542 */
83214501Srpaulo	{AHA1542_PNPCOMPAT,	"Adaptec 1542 compatible"},	/* PNP00A0 */
84214501Srpaulo	{ICU0091_PNP,		"Adaptec AHA-1540/1542 SCSI"},	/* ICU0091 */
85214501Srpaulo	{0}
86214501Srpaulo};
87214501Srpaulo
88214501Srpaulo/*
89214501Srpaulo * I/O ports listed in the order enumerated by the card for certain op codes.
90214501Srpaulo */
91214501Srpaulostatic bus_addr_t aha_board_ports[] =
92214501Srpaulo{
93214501Srpaulo	0x330,
94214501Srpaulo	0x334,
95214501Srpaulo	0x230,
96214501Srpaulo	0x234,
97214501Srpaulo	0x130,
98214501Srpaulo	0x134
99214501Srpaulo};
100214501Srpaulo
101214501Srpaulo/*
102214501Srpaulo * Check if the device can be found at the port given
103214501Srpaulo */
104214501Srpaulostatic int
105214501Srpauloaha_isa_probe(device_t dev)
106214501Srpaulo{
107214501Srpaulo	/*
108214501Srpaulo	 * find unit and check we have that many defined
109214501Srpaulo	 */
110214501Srpaulo	struct	aha_softc *aha = device_get_softc(dev);
111214501Srpaulo	int	error;
112214501Srpaulo	u_long	port_start;
113214501Srpaulo	struct resource *port_res;
114214501Srpaulo	int	port_rid;
115214501Srpaulo	int	drq;
116214501Srpaulo	int	irq;
117214501Srpaulo	config_data_t config_data;
118214501Srpaulo
119214501Srpaulo	aha->dev = dev;
120214501Srpaulo	/* Check isapnp ids */
121214501Srpaulo	if (ISA_PNP_PROBE(device_get_parent(dev), dev, aha_ids) == ENXIO)
122214501Srpaulo		return (ENXIO);
123214501Srpaulo
124214501Srpaulo	port_rid = 0;
125214501Srpaulo	port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid,
126214501Srpaulo	    0, ~0, AHA_NREGS, RF_ACTIVE);
127214501Srpaulo
128214501Srpaulo	if (port_res == NULL)
129214501Srpaulo		return (ENXIO);
130214501Srpaulo
131214501Srpaulo	port_start = rman_get_start(port_res);
132214501Srpaulo	aha_alloc(aha, device_get_unit(dev), rman_get_bustag(port_res),
133214501Srpaulo	    rman_get_bushandle(port_res));
134214501Srpaulo
135214501Srpaulo	/* See if there is really a card present */
136214501Srpaulo	if (aha_probe(aha) || aha_fetch_adapter_info(aha)) {
137214501Srpaulo		aha_free(aha);
138214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
139214501Srpaulo		return (ENXIO);
140214501Srpaulo	}
141214501Srpaulo
142214501Srpaulo	/*
143214501Srpaulo	 * Determine our IRQ, and DMA settings and
144214501Srpaulo	 * export them to the configuration system.
145214501Srpaulo	 */
146214501Srpaulo	error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0,
147214501Srpaulo	    (uint8_t*)&config_data, sizeof(config_data), DEFAULT_CMD_TIMEOUT);
148214501Srpaulo
149214501Srpaulo	if (error != 0) {
150214501Srpaulo		device_printf(dev, "Could not determine IRQ or DMA "
151214501Srpaulo		    "settings for adapter at %#jx.  Failing probe\n",
152214501Srpaulo		    (uintmax_t)port_start);
153214501Srpaulo		aha_free(aha);
154214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, port_rid,
155214501Srpaulo		    port_res);
156214501Srpaulo		return (ENXIO);
157214501Srpaulo	}
158214501Srpaulo
159214501Srpaulo	bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
160214501Srpaulo
161214501Srpaulo	switch (config_data.dma_chan) {
162214501Srpaulo	case DMA_CHAN_5:
163214501Srpaulo		drq = 5;
164214501Srpaulo		break;
165214501Srpaulo	case DMA_CHAN_6:
166214501Srpaulo		drq = 6;
167214501Srpaulo		break;
168214501Srpaulo	case DMA_CHAN_7:
169214501Srpaulo		drq = 7;
170214501Srpaulo		break;
171214501Srpaulo	default:
172214501Srpaulo		device_printf(dev, "Invalid DMA setting for adapter at %#jx.",
173214501Srpaulo		    (uintmax_t)port_start);
174214501Srpaulo		return (ENXIO);
175214501Srpaulo	}
176214501Srpaulo	error = bus_set_resource(dev, SYS_RES_DRQ, 0, drq, 1);
177214501Srpaulo	if (error)
178214501Srpaulo		return error;
179214501Srpaulo
180214501Srpaulo	irq = ffs(config_data.irq) + 8;
181214501Srpaulo	error = bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
182214501Srpaulo	return (error);
183214501Srpaulo}
184214501Srpaulo
185214501Srpaulo/*
186214501Srpaulo * Attach all the sub-devices we can find
187214501Srpaulo */
188214501Srpaulostatic int
189214501Srpauloaha_isa_attach(device_t dev)
190214501Srpaulo{
191214501Srpaulo	struct	aha_softc *aha = device_get_softc(dev);
192214501Srpaulo	bus_dma_filter_t *filter;
193214501Srpaulo	void		 *filter_arg;
194214501Srpaulo	bus_addr_t	 lowaddr;
195214501Srpaulo	void		 *ih;
196214501Srpaulo	int		 error;
197214501Srpaulo
198214501Srpaulo	aha->dev = dev;
199214501Srpaulo	aha->portrid = 0;
200214501Srpaulo	aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &aha->portrid,
201214501Srpaulo	    0, ~0, AHA_NREGS, RF_ACTIVE);
202214501Srpaulo	if (!aha->port) {
203214501Srpaulo		device_printf(dev, "Unable to allocate I/O ports\n");
204214501Srpaulo		return ENOMEM;
205214501Srpaulo	}
206214501Srpaulo
207214501Srpaulo	aha->irqrid = 0;
208214501Srpaulo	aha->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &aha->irqrid,
209214501Srpaulo	    RF_ACTIVE);
210214501Srpaulo	if (!aha->irq) {
211214501Srpaulo		device_printf(dev, "Unable to allocate excluse use of irq\n");
212214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid,
213214501Srpaulo		    aha->port);
214214501Srpaulo		return ENOMEM;
215214501Srpaulo	}
216214501Srpaulo
217214501Srpaulo	aha->drqrid = 0;
218214501Srpaulo	aha->drq = bus_alloc_resource_any(dev, SYS_RES_DRQ, &aha->drqrid,
219214501Srpaulo	    RF_ACTIVE);
220214501Srpaulo	if (!aha->drq) {
221214501Srpaulo		device_printf(dev, "Unable to allocate drq\n");
222214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid,
223214501Srpaulo		    aha->port);
224214501Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
225214501Srpaulo		return ENOMEM;
226214501Srpaulo	}
227214501Srpaulo
228214501Srpaulo#if 0				/* is the drq ever unset? */
229214501Srpaulo	if (dev->id_drq != -1)
230214501Srpaulo		isa_dmacascade(dev->id_drq);
231214501Srpaulo#endif
232214501Srpaulo	isa_dmacascade(rman_get_start(aha->drq));
233214501Srpaulo
234214501Srpaulo	/* Allocate our parent dmatag */
235214501Srpaulo	filter = NULL;
236214501Srpaulo	filter_arg = NULL;
237214501Srpaulo	lowaddr = BUS_SPACE_MAXADDR_24BIT;
238214501Srpaulo
239214501Srpaulo	if (bus_dma_tag_create(	/* parent	*/ NULL,
240214501Srpaulo				/* alignemnt	*/ 1,
241214501Srpaulo				/* boundary	*/ 0,
242214501Srpaulo				/* lowaddr	*/ lowaddr,
243214501Srpaulo				/* highaddr	*/ BUS_SPACE_MAXADDR,
244214501Srpaulo				/* filter	*/ filter,
245214501Srpaulo				/* filterarg	*/ filter_arg,
246214501Srpaulo				/* maxsize	*/ BUS_SPACE_MAXSIZE_24BIT,
247214501Srpaulo				/* nsegments	*/ ~0,
248214501Srpaulo				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_24BIT,
249214501Srpaulo				/* flags	*/ 0,
250214501Srpaulo				/* lockfunc	*/ busdma_lock_mutex,
251214501Srpaulo				/* lockarg	*/ &Giant,
252214501Srpaulo				&aha->parent_dmat) != 0) {
253214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid,
254214501Srpaulo		    aha->port);
255214501Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
256214501Srpaulo		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
257214501Srpaulo		aha_free(aha);
258214501Srpaulo		return (ENOMEM);
259214501Srpaulo	}
260214501Srpaulo
261214501Srpaulo	if (aha_init(aha)) {
262214501Srpaulo		device_printf(dev, "init failed\n");
263214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid,
264214501Srpaulo		    aha->port);
265214501Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
266214501Srpaulo		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
267214501Srpaulo		aha_free(aha);
268214501Srpaulo		return (ENOMEM);
269214501Srpaulo	}
270214501Srpaulo	/*
271214501Srpaulo	 * The 1542A and B look the same.  So we guess based on
272214501Srpaulo	 * the firmware revision.  It appears that only rev 0 is on
273214501Srpaulo	 * the A cards.
274214501Srpaulo	 */
275214501Srpaulo	if (aha->boardid <= BOARD_1542 && aha->fw_major == 0) {
276214501Srpaulo		device_printf(dev, "154xA may not work\n");
277214501Srpaulo		aha->ccb_sg_opcode = INITIATOR_SG_CCB;
278214501Srpaulo		aha->ccb_ccb_opcode = INITIATOR_CCB;
279214501Srpaulo	}
280214501Srpaulo
281214501Srpaulo	error = aha_attach(aha);
282214501Srpaulo	if (error) {
283214501Srpaulo		device_printf(dev, "attach failed\n");
284214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid,
285214501Srpaulo		    aha->port);
286214501Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
287214501Srpaulo		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
288214501Srpaulo		aha_free(aha);
289214501Srpaulo		return (error);
290214501Srpaulo	}
291214501Srpaulo
292214501Srpaulo	error = bus_setup_intr(dev, aha->irq, INTR_TYPE_CAM|INTR_ENTROPY,
293214501Srpaulo	    aha_intr, aha, &ih);
294214501Srpaulo	if (error) {
295214501Srpaulo		device_printf(dev, "Unable to register interrupt handler\n");
296214501Srpaulo		bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid,
297214501Srpaulo		    aha->port);
298214501Srpaulo		bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
299214501Srpaulo		bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
300214501Srpaulo		aha_free(aha);
301214501Srpaulo		return (error);
302214501Srpaulo	}
303214501Srpaulo
304214501Srpaulo	return (0);
305214501Srpaulo}
306214501Srpaulo
307214501Srpaulostatic int
308214501Srpauloaha_isa_detach(device_t dev)
309214501Srpaulo{
310214501Srpaulo	struct aha_softc *aha = (struct aha_softc *)device_get_softc(dev);
311214501Srpaulo	int error;
312214501Srpaulo
313214501Srpaulo	error = bus_teardown_intr(dev, aha->irq, aha->ih);
314214501Srpaulo	if (error) {
315214501Srpaulo		device_printf(dev, "failed to unregister interrupt handler\n");
316214501Srpaulo	}
317214501Srpaulo
318214501Srpaulo	bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port);
319214501Srpaulo	bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq);
320214501Srpaulo	bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq);
321214501Srpaulo
322214501Srpaulo	error = aha_detach(aha);
323214501Srpaulo	if (error) {
324214501Srpaulo		device_printf(dev, "detach failed\n");
325214501Srpaulo		return (error);
326214501Srpaulo	}
327214501Srpaulo	aha_free(aha);
328214501Srpaulo
329214501Srpaulo	return (0);
330214501Srpaulo}
331214501Srpaulo
332214501Srpaulostatic void
333214501Srpauloaha_isa_identify(driver_t *driver, device_t parent)
334214501Srpaulo{
335214501Srpaulo	int i;
336214501Srpaulo	bus_addr_t ioport;
337214501Srpaulo	struct aha_softc aha;
338214501Srpaulo	int rid;
339214501Srpaulo	struct resource *res;
340214501Srpaulo	device_t child;
341214501Srpaulo
342214501Srpaulo	/* Attempt to find an adapter */
343214501Srpaulo	for (i = 0; i < sizeof(aha_board_ports) / sizeof(aha_board_ports[0]);
344214501Srpaulo	    i++) {
345214501Srpaulo		bzero(&aha, sizeof(aha));
346214501Srpaulo		ioport = aha_board_ports[i];
347214501Srpaulo		/*
348214501Srpaulo		 * XXX Check to see if we have a hard-wired aha device at
349214501Srpaulo		 * XXX this port, if so, skip.  This should also cover the
350214501Srpaulo		 * XXX case where we are run multiple times due to, eg,
351214501Srpaulo		 * XXX kldload/kldunload.
352214501Srpaulo		 */
353214501Srpaulo		rid = 0;
354214501Srpaulo		res = bus_alloc_resource(parent, SYS_RES_IOPORT, &rid,
355214501Srpaulo		    ioport, ioport, AHA_NREGS, RF_ACTIVE);
356214501Srpaulo		if (res == NULL)
357214501Srpaulo			continue;
358214501Srpaulo		aha_alloc(&aha, -1, rman_get_bustag(res),
359214501Srpaulo		    rman_get_bushandle(res));
360214501Srpaulo		/* See if there is really a card present */
361214501Srpaulo		if (aha_probe(&aha) || aha_fetch_adapter_info(&aha))
362214501Srpaulo			goto not_this_one;
363214501Srpaulo		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "aha", -1);
364214501Srpaulo		bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, AHA_NREGS);
365214501Srpaulo		/*
366214501Srpaulo		 * Could query the board and set IRQ/DRQ, but probe does
367214501Srpaulo		 * that.
368214501Srpaulo		 */
369214501Srpaulo	not_this_one:;
370214501Srpaulo		bus_release_resource(parent, SYS_RES_IOPORT, rid, res);
371214501Srpaulo		aha_free(&aha);
372214501Srpaulo	}
373214501Srpaulo}
374214501Srpaulo
375214501Srpaulostatic device_method_t aha_isa_methods[] = {
376214501Srpaulo	/* Device interface */
377214501Srpaulo	DEVMETHOD(device_probe,		aha_isa_probe),
378214501Srpaulo	DEVMETHOD(device_attach,	aha_isa_attach),
379214501Srpaulo	DEVMETHOD(device_detach,	aha_isa_detach),
380214501Srpaulo	DEVMETHOD(device_identify,	aha_isa_identify),
381214501Srpaulo
382214501Srpaulo	{ 0, 0 }
383214501Srpaulo};
384214501Srpaulo
385214501Srpaulostatic driver_t aha_isa_driver = {
386214501Srpaulo	"aha",
387214501Srpaulo	aha_isa_methods,
388214501Srpaulo	sizeof(struct aha_softc),
389214501Srpaulo};
390214501Srpaulo
391214501Srpaulostatic devclass_t aha_devclass;
392214501Srpaulo
393214501SrpauloDRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, 0, 0);
394214501Srpaulo