aha_isa.c revision 52133
1/*
2 * Product specific probe and attach routines for:
3 *      Adaptec 154x.
4 *
5 * Derived from code written by:
6 *
7 * Copyright (c) 1998 Justin T. Gibbs
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions, and the following disclaimer,
15 *    without modification, immediately at the beginning of the file.
16 * 2. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: head/sys/dev/aha/aha_isa.c 52133 1999-10-11 19:05:21Z imp $
32 */
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37
38#include <machine/bus_pio.h>
39#include <machine/bus.h>
40#include <machine/resource.h>
41#include <sys/module.h>
42#include <sys/bus.h>
43#include <sys/rman.h>
44
45#include <isa/isavar.h>
46
47#include <dev/aha/ahareg.h>
48
49#include <cam/scsi/scsi_all.h>
50
51static struct isa_pnp_id aha_ids[] = {
52	{AHA1542_PNP,		NULL},		/* ADP1542 */
53	{AHA1542_PNPCOMPAT,	NULL},		/* PNP00A0 */
54	{0}
55};
56
57/*
58 * Check if the device can be found at the port given
59 * and if so, set it up ready for further work
60 * as an argument, takes the isa_device structure from
61 * autoconf.c
62 */
63static int
64aha_isa_probe(device_t dev)
65{
66	/*
67	 * find unit and check we have that many defined
68	 */
69	struct	aha_softc **sc = device_get_softc(dev);
70	struct	aha_softc *aha;
71	int	port_index;
72	int	max_port_index;
73	int	error;
74	u_long	port_start, port_count;
75	struct resource *port_res;
76	int	port_rid;
77	int	drq;
78	int	irq;
79
80	aha = NULL;
81
82	/* Check isapnp ids */
83	if (ISA_PNP_PROBE(device_get_parent(dev), dev, aha_ids) == ENXIO)
84		return (ENXIO);
85
86	error = ISA_GET_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT,
87	    0, &port_start, &port_count);
88	if (error != 0)
89		port_start = 0;
90
91	/*
92	 * Bound our board search if the user has
93	 * specified an exact port.
94	 */
95	aha_find_probe_range(port_start, &port_index, &max_port_index);
96
97	if (port_index < 0)
98		return ENXIO;
99
100	/* Attempt to find an adapter */
101	for (;port_index <= max_port_index; port_index++) {
102		config_data_t config_data;
103		u_int ioport;
104		int error;
105
106		ioport = aha_iop_from_bio(port_index);
107
108		/*
109		 * Ensure this port has not already been claimed already
110		 * by another adapter.
111		 */
112		if (aha_check_probed_iop(ioport) != 0)
113			continue;
114		error = ISA_SET_RESOURCE(device_get_parent(dev), dev,
115		    SYS_RES_IOPORT, 0, ioport, AHA_NREGS);
116		if (error)
117			return error;
118
119		port_rid = 0;
120		port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid,
121		    0, ~0, AHA_NREGS, RF_ACTIVE);
122		if (!port_res)
123			continue;
124
125		/* Allocate a softc for use during probing */
126		aha = aha_alloc(device_get_unit(dev), rman_get_bustag(port_res),
127		    rman_get_bushandle(port_res));
128
129		if (aha == NULL) {
130			bus_release_resource(dev, SYS_RES_IOPORT, port_rid,
131			    port_res);
132			break;
133		}
134
135		/* We're going to attempt to probe it now, so mark it probed */
136		aha_mark_probed_bio(port_index);
137
138		/* See if there is really a card present */
139		if (aha_probe(aha) || aha_fetch_adapter_info(aha)) {
140			aha_free(aha);
141			bus_release_resource(dev, SYS_RES_IOPORT, port_rid,
142			    port_res);
143			continue;
144		}
145
146		/*
147		 * Determine our IRQ, and DMA settings and
148		 * export them to the configuration system.
149		 */
150		error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0,
151		    (u_int8_t*)&config_data, sizeof(config_data),
152		    DEFAULT_CMD_TIMEOUT);
153
154		if (error != 0) {
155			printf("aha_isa_probe: Could not determine IRQ or DMA "
156			    "settings for adapter at 0x%x.  Failing probe\n",
157			    ioport);
158			aha_free(aha);
159			bus_release_resource(dev, SYS_RES_IOPORT, port_rid,
160			    port_res);
161			continue;
162		}
163
164		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
165
166		switch (config_data.dma_chan) {
167		case DMA_CHAN_5:
168			drq = 5;
169			break;
170		case DMA_CHAN_6:
171			drq = 6;
172			break;
173		case DMA_CHAN_7:
174			drq = 7;
175			break;
176		default:
177			printf("aha_isa_probe: Invalid DMA setting "
178			    "detected for adapter at 0x%x.  "
179			    "Failing probe\n", ioport);
180			return (ENXIO);
181		}
182		error = ISA_SET_RESOURCE(device_get_parent(dev), dev,
183		    SYS_RES_DRQ, 0, drq, 1);
184		if (error)
185			return error;
186
187		irq = ffs(config_data.irq) + 8;
188		error = ISA_SET_RESOURCE(device_get_parent(dev), dev,
189		    SYS_RES_IRQ, 0, irq, 1);
190		if (error)
191			return error;
192
193		*sc = aha;
194		aha_unit++;
195
196		return (0);
197	}
198
199	return (ENXIO);
200}
201
202/*
203 * Attach all the sub-devices we can find
204 */
205static int
206aha_isa_attach(device_t dev)
207{
208	struct	aha_softc **sc = device_get_softc(dev);
209	struct	aha_softc *aha;
210	bus_dma_filter_t *filter;
211	void		 *filter_arg;
212	bus_addr_t	 lowaddr;
213	struct resource	 *port_res;
214	int		 port_rid;
215	struct resource	 *irq_res;
216	int		 irq_rid;
217	struct resource	 *drq_res;
218	int		 drq_rid;
219	void		 *ih;
220	int		 error;
221
222	port_rid = 0;
223	port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid,
224	    0, ~0, AHA_NREGS, RF_ACTIVE);
225	if (!port_res) {
226		device_printf(dev, "Unable to allocate I/O ports\n");
227		return ENOMEM;
228	}
229
230	irq_rid = 0;
231	irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &irq_rid, 0, ~0, 1,
232	    RF_ACTIVE);
233	if (!irq_res) {
234		device_printf(dev, "Unable to allocate excluse use of irq\n");
235		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
236		return ENOMEM;
237	}
238
239	drq_rid = 0;
240	drq_res = bus_alloc_resource(dev, SYS_RES_DRQ, &drq_rid, 0, ~0, 1,
241	    RF_ACTIVE);
242	if (!drq_res) {
243		device_printf(dev, "Unable to allocate drq\n");
244		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
245		bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res);
246		return ENOMEM;
247	}
248
249	aha = *sc;
250#if 0				/* is the drq ever unset? */
251	if (dev->id_drq != -1)
252		isa_dmacascade(dev->id_drq);
253#endif
254	isa_dmacascade(rman_get_start(drq_res));
255
256	/* Allocate our parent dmatag */
257	filter = NULL;
258	filter_arg = NULL;
259	lowaddr = BUS_SPACE_MAXADDR_24BIT;
260
261	if (bus_dma_tag_create(/*parent*/NULL, /*alignemnt*/1, /*boundary*/0,
262	    lowaddr, /*highaddr*/BUS_SPACE_MAXADDR,
263	    filter, filter_arg,
264	    /*maxsize*/BUS_SPACE_MAXSIZE_24BIT,
265	    /*nsegments*/BUS_SPACE_UNRESTRICTED,
266	    /*maxsegsz*/BUS_SPACE_MAXSIZE_24BIT,
267	    /*flags*/0, &aha->parent_dmat) != 0) {
268                aha_free(aha);
269		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
270		bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res);
271		bus_release_resource(dev, SYS_RES_DRQ, drq_rid, drq_res);
272                return (ENOMEM);
273        }
274
275        if (aha_init(aha)) {
276		device_printf(dev, "init failed\n");
277                aha_free(aha);
278		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
279		bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res);
280		bus_release_resource(dev, SYS_RES_DRQ, drq_rid, drq_res);
281                return (ENOMEM);
282        }
283
284	error = aha_attach(aha);
285	if (error) {
286		device_printf(dev, "attach failed\n");
287                aha_free(aha);
288		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
289		bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res);
290		bus_release_resource(dev, SYS_RES_DRQ, drq_rid, drq_res);
291                return (error);
292	}
293
294	error = bus_setup_intr(dev, irq_res, INTR_TYPE_CAM, aha_intr, aha,
295	    &ih);
296	if (error) {
297		device_printf(dev, "Unable to register interrupt handler\n");
298                aha_free(aha);
299		bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res);
300		bus_release_resource(dev, SYS_RES_IRQ, irq_rid, irq_res);
301		bus_release_resource(dev, SYS_RES_DRQ, drq_rid, drq_res);
302                return (error);
303	}
304
305	return (0);
306}
307
308static device_method_t aha_isa_methods[] = {
309	/* Device interface */
310	DEVMETHOD(device_probe,		aha_isa_probe),
311	DEVMETHOD(device_attach,	aha_isa_attach),
312
313	{ 0, 0 }
314};
315
316static driver_t aha_isa_driver = {
317	"aha",
318	aha_isa_methods,
319	sizeof(struct aha_softc*),
320};
321
322static devclass_t aha_devclass;
323
324DRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, 0, 0);
325