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
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	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_anywhere(dev, SYS_RES_IOPORT, &port_rid,
124	    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 */
186static int
187aha_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_anywhere(dev, SYS_RES_IOPORT,
195	    &aha->portrid, 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);
272fail: ;
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
280static int
281aha_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
303static void
304aha_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 < nitems(aha_board_ports); i++) {
314		bzero(&aha, sizeof(aha));
315		ioport = aha_board_ports[i];
316		/*
317		 * XXX Check to see if we have a hard-wired aha device at
318		 * XXX this port, if so, skip.  This should also cover the
319		 * XXX case where we are run multiple times due to, eg,
320		 * XXX kldload/kldunload.
321		 */
322		rid = 0;
323		aha.port = bus_alloc_resource(parent, SYS_RES_IOPORT, &rid,
324		    ioport, ioport, AHA_NREGS, RF_ACTIVE);
325		if (aha.port == NULL)
326			continue;
327		aha_alloc(&aha);
328		/* See if there is really a card present */
329		if (aha_probe(&aha) || aha_fetch_adapter_info(&aha))
330			goto not_this_one;
331		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "aha", -1);
332		bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, AHA_NREGS);
333		/*
334		 * Could query the board and set IRQ/DRQ, but probe does
335		 * that.
336		 */
337	not_this_one:
338		bus_release_resource(parent, SYS_RES_IOPORT, rid, aha.port);
339		aha_free(&aha);
340	}
341}
342
343static device_method_t aha_isa_methods[] = {
344	/* Device interface */
345	DEVMETHOD(device_probe,		aha_isa_probe),
346	DEVMETHOD(device_attach,	aha_isa_attach),
347	DEVMETHOD(device_detach,	aha_isa_detach),
348	DEVMETHOD(device_identify,	aha_isa_identify),
349
350	{ 0, 0 }
351};
352
353static driver_t aha_isa_driver = {
354	"aha",
355	aha_isa_methods,
356	sizeof(struct aha_softc),
357};
358
359static devclass_t aha_devclass;
360
361DRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, 0, 0);
362MODULE_DEPEND(aha, isa, 1, 1, 1);
363