1151497Sru/*
275584Sru * Product specific probe and attach routines for:
3151497Sru *      Adaptec 154x.
4151497Sru */
575584Sru/*-
675584Sru * Copyright (c) 1999-2003 M. Warner Losh
775584Sru * All rights reserved.
875584Sru *
975584Sru * Redistribution and use in source and binary forms, with or without
1075584Sru * modification, are permitted provided that the following conditions
1175584Sru * are met:
1275584Sru * 1. Redistributions of source code must retain the above copyright
1375584Sru *    notice, this list of conditions, and the following disclaimer,
1475584Sru *    without modification, immediately at the beginning of the file.
1575584Sru * 2. The name of the author may not be used to endorse or promote products
1675584Sru *    derived from this software without specific prior written permission.
1775584Sru *
1875584Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1975584Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2075584Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21151497Sru * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2275584Sru * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23151497Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24151497Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25151497Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26151497Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27151497Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28151497Sru * SUCH DAMAGE.
29151497Sru *
30151497Sru * Derived from bt isa from end, written by:
31151497Sru *
32151497Sru * Copyright (c) 1998 Justin T. Gibbs
33151497Sru * All rights reserved.
34151497Sru *
35151497Sru * Redistribution and use in source and binary forms, with or without
36151497Sru * modification, are permitted provided that the following conditions
37151497Sru * are met:
38151497Sru * 1. Redistributions of source code must retain the above copyright
39151497Sru *    notice, this list of conditions, and the following disclaimer,
40151497Sru *    without modification, immediately at the beginning of the file.
41151497Sru * 2. The name of the author may not be used to endorse or promote products
42151497Sru *    derived from this software without specific prior written permission.
43151497Sru *
44151497Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45151497Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46151497Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47151497Sru * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
48151497Sru * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49151497Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50151497Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51151497Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52151497Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53151497Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54151497Sru * SUCH DAMAGE.
55151497Sru */
56151497Sru
57151497Sru#include <sys/cdefs.h>
58151497Sru__FBSDID("$FreeBSD$");
59151497Sru
60151497Sru#include <sys/param.h>
61151497Sru#include <sys/systm.h>
62151497Sru#include <sys/kernel.h>
63151497Sru#include <sys/lock.h>
64151497Sru#include <sys/mutex.h>
65151497Sru
66151497Sru#include <machine/bus.h>
67151497Sru#include <machine/resource.h>
68151497Sru#include <sys/module.h>
69151497Sru#include <sys/bus.h>
70151497Sru#include <sys/rman.h>
71151497Sru
72151497Sru#include <isa/isavar.h>
73151497Sru
74151497Sru#include <dev/aha/ahareg.h>
75151497Sru
76151497Sru#include <cam/scsi/scsi_all.h>
77151497Sru
78151497Srustatic struct isa_pnp_id aha_ids[] = {
79151497Sru	{ADP0100_PNP,		"Adaptec 1540/1542 ISA SCSI"},	/* ADP0100 */
80151497Sru	{AHA1540_PNP,		"Adaptec 1540/aha-1640/aha-1535"},/* ADP1540 */
81151497Sru	{AHA1542_PNP,		"Adaptec 1542/aha-1535"},	/* ADP1542 */
82151497Sru	{AHA1542_PNPCOMPAT,	"Adaptec 1542 compatible"},	/* PNP00A0 */
83151497Sru	{ICU0091_PNP,		"Adaptec AHA-1540/1542 SCSI"},	/* ICU0091 */
84151497Sru	{0}
85151497Sru};
86151497Sru
87151497Sru/*
88151497Sru * I/O ports listed in the order enumerated by the card for certain op codes.
89151497Sru */
90151497Srustatic bus_addr_t aha_board_ports[] =
91151497Sru{
92151497Sru	0x330,
93151497Sru	0x334,
94151497Sru	0x230,
95151497Sru	0x234,
96151497Sru	0x130,
97151497Sru	0x134
98151497Sru};
99151497Sru
100151497Sru/*
101151497Sru * Check if the device can be found at the port given
102151497Sru */
103151497Srustatic int
104151497Sruaha_isa_probe(device_t dev)
105151497Sru{
106151497Sru	/*
107151497Sru	 * find unit and check we have that many defined
108151497Sru	 */
109151497Sru	struct	aha_softc *aha = device_get_softc(dev);
110151497Sru	int	error;
111151497Sru	u_long	port_start;
112151497Sru	int	port_rid;
113151497Sru	int	drq;
114151497Sru	int	irq;
115151497Sru	config_data_t config_data;
116151497Sru
117151497Sru	aha->dev = dev;
118151497Sru	/* Check isapnp ids */
119151497Sru	if (ISA_PNP_PROBE(device_get_parent(dev), dev, aha_ids) == ENXIO)
120151497Sru		return (ENXIO);
121151497Sru
122151497Sru	port_rid = 0;
123151497Sru	aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid,
124151497Sru	    0ul, ~0ul, AHA_NREGS, RF_ACTIVE);
125151497Sru
126151497Sru	if (aha->port == NULL)
127151497Sru		return (ENXIO);
128151497Sru
129151497Sru	port_start = rman_get_start(aha->port);
130151497Sru	aha_alloc(aha);
131151497Sru
132151497Sru	/* See if there is really a card present */
133151497Sru	if (aha_probe(aha) || aha_fetch_adapter_info(aha)) {
134151497Sru		aha_free(aha);
135151497Sru		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, aha->port);
136151497Sru		return (ENXIO);
137151497Sru	}
138151497Sru
139151497Sru	/*
140151497Sru	 * Determine our IRQ, and DMA settings and
141151497Sru	 * export them to the configuration system.
142151497Sru	 */
143151497Sru	error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0,
144151497Sru	    (uint8_t*)&config_data, sizeof(config_data), DEFAULT_CMD_TIMEOUT);
145151497Sru
146151497Sru	if (error != 0) {
147151497Sru		device_printf(dev, "Could not determine IRQ or DMA "
148151497Sru		    "settings for adapter at %#jx.  Failing probe\n",
149151497Sru		    (uintmax_t)port_start);
150151497Sru		aha_free(aha);
151151497Sru		bus_release_resource(dev, SYS_RES_IOPORT, port_rid,
152151497Sru		    aha->port);
153151497Sru		return (ENXIO);
154151497Sru	}
155151497Sru
156151497Sru	bus_release_resource(dev, SYS_RES_IOPORT, port_rid, aha->port);
157151497Sru	aha->port = NULL;
158151497Sru
159151497Sru	switch (config_data.dma_chan) {
160151497Sru	case DMA_CHAN_5:
161151497Sru		drq = 5;
162151497Sru		break;
163151497Sru	case DMA_CHAN_6:
164151497Sru		drq = 6;
165151497Sru		break;
166151497Sru	case DMA_CHAN_7:
167151497Sru		drq = 7;
168151497Sru		break;
169151497Sru	default:
170151497Sru		device_printf(dev, "Invalid DMA setting for adapter at %#jx.",
171151497Sru		    (uintmax_t)port_start);
172151497Sru		return (ENXIO);
173151497Sru	}
174151497Sru	error = bus_set_resource(dev, SYS_RES_DRQ, 0, drq, 1);
175151497Sru	if (error)
176151497Sru		return error;
177151497Sru
178151497Sru	irq = ffs(config_data.irq) + 8;
179151497Sru	error = bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
180151497Sru	return (error);
181151497Sru}
182151497Sru
183151497Sru/*
184151497Sru * Attach all the sub-devices we can find
185151497Sru */
186151497Srustatic int
187151497Sruaha_isa_attach(device_t dev)
188151497Sru{
189151497Sru	struct	aha_softc *aha = device_get_softc(dev);
190151497Sru	int		 error = ENOMEM;
191151497Sru
192151497Sru	aha->dev = dev;
193151497Sru	aha->portrid = 0;
194151497Sru	aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &aha->portrid,
195151497Sru	    0ul, ~0ul, AHA_NREGS, RF_ACTIVE);
196151497Sru	if (!aha->port) {
197151497Sru		device_printf(dev, "Unable to allocate I/O ports\n");
198151497Sru		goto fail;
199151497Sru	}
200151497Sru
201151497Sru	aha->irqrid = 0;
202151497Sru	aha->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &aha->irqrid,
203151497Sru	    RF_ACTIVE);
204151497Sru	if (!aha->irq) {
205151497Sru		device_printf(dev, "Unable to allocate excluse use of irq\n");
206151497Sru		goto fail;
207151497Sru	}
208151497Sru
209151497Sru	aha->drqrid = 0;
210151497Sru	aha->drq = bus_alloc_resource_any(dev, SYS_RES_DRQ, &aha->drqrid,
211151497Sru	    RF_ACTIVE);
212151497Sru	if (!aha->drq) {
213151497Sru		device_printf(dev, "Unable to allocate drq\n");
214151497Sru		goto fail;
215151497Sru	}
216151497Sru
217151497Sru#if 0				/* is the drq ever unset? */
218151497Sru	if (dev->id_drq != -1)
219151497Sru		isa_dmacascade(dev->id_drq);
220151497Sru#endif
221151497Sru	isa_dmacascade(rman_get_start(aha->drq));
222151497Sru
223151497Sru	/* Allocate our parent dmatag */
224151497Sru	if (bus_dma_tag_create(	/* parent	*/ bus_get_dma_tag(dev),
225151497Sru				/* alignemnt	*/ 1,
226151497Sru				/* boundary	*/ 0,
227151497Sru				/* lowaddr	*/ BUS_SPACE_MAXADDR_24BIT,
228151497Sru				/* highaddr	*/ BUS_SPACE_MAXADDR,
229151497Sru				/* filter	*/ NULL,
230151497Sru				/* filterarg	*/ NULL,
231151497Sru				/* maxsize	*/ BUS_SPACE_MAXSIZE_24BIT,
232151497Sru				/* nsegments	*/ ~0,
233151497Sru				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_24BIT,
234151497Sru				/* flags	*/ 0,
235151497Sru				/* lockfunc	*/ NULL,
236151497Sru				/* lockarg	*/ NULL,
237151497Sru				&aha->parent_dmat) != 0) {
238151497Sru		device_printf(dev, "dma tag create failed.\n");
239151497Sru		goto fail;
240151497Sru        }
241151497Sru
242151497Sru	if (aha_init(aha)) {
243151497Sru		device_printf(dev, "init failed\n");
244151497Sru		goto fail;
245151497Sru        }
246151497Sru	/*
247151497Sru	 * The 1542A and B look the same.  So we guess based on
248151497Sru	 * the firmware revision.  It appears that only rev 0 is on
249151497Sru	 * the A cards.
250151497Sru	 */
251151497Sru	if (aha->boardid <= BOARD_1542 && aha->fw_major == 0) {
252151497Sru		device_printf(dev, "154xA may not work\n");
253151497Sru		aha->ccb_sg_opcode = INITIATOR_SG_CCB;
254151497Sru		aha->ccb_ccb_opcode = INITIATOR_CCB;
255151497Sru	}
256151497Sru
257151497Sru	error = aha_attach(aha);
258151497Sru	if (error) {
259151497Sru		device_printf(dev, "attach failed\n");
260151497Sru		goto fail;
261151497Sru	}
262151497Sru
263151497Sru	error = bus_setup_intr(dev, aha->irq, INTR_TYPE_CAM|INTR_ENTROPY|
264151497Sru	    INTR_MPSAFE, NULL, aha_intr, aha, &aha->ih);
265151497Sru	if (error) {
266151497Sru		device_printf(dev, "Unable to register interrupt handler\n");
267151497Sru		aha_detach(aha);
268151497Sru                goto fail;
269151497Sru	}
270151497Sru
271151497Sru	return (0);
272151497Srufail: ;
273151497Sru	aha_free(aha);
274151497Sru	bus_free_resource(dev, SYS_RES_IOPORT, aha->port);
275151497Sru	bus_free_resource(dev, SYS_RES_IRQ, aha->irq);
276151497Sru	bus_free_resource(dev, SYS_RES_DRQ, aha->drq);
277151497Sru	return (error);
278151497Sru}
279151497Sru
280151497Srustatic int
281151497Sruaha_isa_detach(device_t dev)
282151497Sru{
283151497Sru	struct aha_softc *aha = (struct aha_softc *)device_get_softc(dev);
284151497Sru	int error;
285151497Sru
286151497Sru	error = bus_teardown_intr(dev, aha->irq, aha->ih);
287151497Sru	if (error)
288151497Sru		device_printf(dev, "failed to unregister interrupt handler\n");
289151497Sru
290151497Sru	error = aha_detach(aha);
291151497Sru	if (error) {
292151497Sru		device_printf(dev, "detach failed\n");
293151497Sru		return (error);
294151497Sru	}
295151497Sru	aha_free(aha);
296151497Sru	bus_free_resource(dev, SYS_RES_IOPORT, aha->port);
297151497Sru	bus_free_resource(dev, SYS_RES_IRQ, aha->irq);
298151497Sru	bus_free_resource(dev, SYS_RES_DRQ, aha->drq);
299151497Sru
300151497Sru	return (0);
301151497Sru}
302151497Sru
303151497Srustatic void
304151497Sruaha_isa_identify(driver_t *driver, device_t parent)
305151497Sru{
306151497Sru	int i;
307151497Sru	bus_addr_t ioport;
308151497Sru	struct aha_softc aha;
309151497Sru	int rid;
310151497Sru	device_t child;
311151497Sru
312151497Sru	/* Attempt to find an adapter */
313151497Sru	for (i = 0; i < sizeof(aha_board_ports) / sizeof(aha_board_ports[0]);
314151497Sru	    i++) {
315151497Sru		bzero(&aha, sizeof(aha));
316151497Sru		ioport = aha_board_ports[i];
317151497Sru		/*
318151497Sru		 * XXX Check to see if we have a hard-wired aha device at
319151497Sru		 * XXX this port, if so, skip.  This should also cover the
320151497Sru		 * XXX case where we are run multiple times due to, eg,
321151497Sru		 * XXX kldload/kldunload.
322151497Sru		 */
323151497Sru		rid = 0;
324151497Sru		aha.port = bus_alloc_resource(parent, SYS_RES_IOPORT, &rid,
325151497Sru		    ioport, ioport, AHA_NREGS, RF_ACTIVE);
326151497Sru		if (aha.port == NULL)
327151497Sru			continue;
328151497Sru		aha_alloc(&aha);
329151497Sru		/* See if there is really a card present */
330151497Sru		if (aha_probe(&aha) || aha_fetch_adapter_info(&aha))
331151497Sru			goto not_this_one;
332151497Sru		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "aha", -1);
333151497Sru		bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, AHA_NREGS);
334151497Sru		/*
335151497Sru		 * Could query the board and set IRQ/DRQ, but probe does
336151497Sru		 * that.
337151497Sru		 */
338151497Sru	not_this_one:
339151497Sru		bus_release_resource(parent, SYS_RES_IOPORT, rid, aha.port);
340151497Sru		aha_free(&aha);
341151497Sru	}
342151497Sru}
343151497Sru
344151497Srustatic device_method_t aha_isa_methods[] = {
345151497Sru	/* Device interface */
346151497Sru	DEVMETHOD(device_probe,		aha_isa_probe),
347151497Sru	DEVMETHOD(device_attach,	aha_isa_attach),
348151497Sru	DEVMETHOD(device_detach,	aha_isa_detach),
349151497Sru	DEVMETHOD(device_identify,	aha_isa_identify),
350151497Sru
351151497Sru	{ 0, 0 }
352151497Sru};
353151497Sru
354151497Srustatic driver_t aha_isa_driver = {
355151497Sru	"aha",
356151497Sru	aha_isa_methods,
357151497Sru	sizeof(struct aha_softc),
358151497Sru};
359151497Sru
360151497Srustatic devclass_t aha_devclass;
361151497Sru
362151497SruDRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, 0, 0);
363151497SruMODULE_DEPEND(aha, isa, 1, 1, 1);
364151497Sru