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