bt_isa.c revision 119418
1250008Sadrian/*
2250008Sadrian * Product specific probe and attach routines for:
3250008Sadrian *      Buslogic BT-54X and BT-445 cards
4250008Sadrian *
5250008Sadrian * Copyright (c) 1998, 1999 Justin T. Gibbs
6250008Sadrian * All rights reserved.
7250008Sadrian *
8250008Sadrian * Redistribution and use in source and binary forms, with or without
9250008Sadrian * modification, are permitted provided that the following conditions
10250008Sadrian * are met:
11250008Sadrian * 1. Redistributions of source code must retain the above copyright
12250008Sadrian *    notice, this list of conditions, and the following disclaimer,
13250008Sadrian *    without modification, immediately at the beginning of the file.
14250008Sadrian * 2. The name of the author may not be used to endorse or promote products
15250008Sadrian *    derived from this software without specific prior written permission.
16250008Sadrian *
17250008Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18250008Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19250008Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20250008Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21250008Sadrian * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22250008Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23250008Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24250008Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25250008Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26250008Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27250008Sadrian * SUCH DAMAGE.
28250008Sadrian *
29250008Sadrian */
30250008Sadrian
31250008Sadrian#include <sys/cdefs.h>
32250008Sadrian__FBSDID("$FreeBSD: head/sys/dev/buslogic/bt_isa.c 119418 2003-08-24 17:55:58Z obrien $");
33250008Sadrian
34250008Sadrian#include <sys/param.h>
35250008Sadrian#include <sys/systm.h>
36250008Sadrian#include <sys/kernel.h>
37250008Sadrian#include <sys/module.h>
38250008Sadrian#include <sys/lock.h>
39250008Sadrian#include <sys/mutex.h>
40250008Sadrian#include <sys/bus.h>
41250008Sadrian
42250008Sadrian#include <machine/bus_pio.h>
43250008Sadrian#include <machine/bus.h>
44250008Sadrian#include <machine/resource.h>
45250008Sadrian#include <sys/rman.h>
46250008Sadrian
47250008Sadrian#include <isa/isavar.h>
48250008Sadrian#include <dev/buslogic/btreg.h>
49250008Sadrian
50250008Sadrian#include <cam/scsi/scsi_all.h>
51250008Sadrian
52250008Sadrianstatic	bus_dma_filter_t btvlbouncefilter;
53250008Sadrianstatic	bus_dmamap_callback_t btmapsensebuffers;
54250008Sadrian
55250008Sadrianstatic int
56250008Sadrianbt_isa_alloc_resources(device_t dev, u_long portstart, u_long portend)
57250008Sadrian{
58250008Sadrian	int rid;
59250008Sadrian	struct resource *port;
60250008Sadrian	struct resource *irq;
61250008Sadrian	struct resource *drq;
62250008Sadrian
63250008Sadrian	rid = 0;
64250008Sadrian	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
65250008Sadrian				  portstart, portend, BT_NREGS, RF_ACTIVE);
66250008Sadrian	if (!port)
67250008Sadrian		return (ENOMEM);
68250008Sadrian
69250008Sadrian	if (isa_get_irq(dev) != -1) {
70250008Sadrian		rid = 0;
71250008Sadrian		irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
72250008Sadrian					 0, ~0, 1, RF_ACTIVE);
73250008Sadrian		if (!irq) {
74250008Sadrian			if (port)
75250008Sadrian				bus_release_resource(dev, SYS_RES_IOPORT,
76250008Sadrian						     0, port);
77250008Sadrian			return (ENOMEM);
78250008Sadrian		}
79250008Sadrian	} else
80250008Sadrian		irq = 0;
81250008Sadrian
82250008Sadrian	if (isa_get_drq(dev) != -1) {
83250008Sadrian		rid = 0;
84250008Sadrian		drq = bus_alloc_resource(dev, SYS_RES_DRQ, &rid,
85250008Sadrian					 0, ~0, 1, RF_ACTIVE);
86250008Sadrian		if (!drq) {
87250008Sadrian			if (port)
88250008Sadrian				bus_release_resource(dev, SYS_RES_IOPORT,
89250008Sadrian						     0, port);
90250008Sadrian			if (irq)
91250008Sadrian				bus_release_resource(dev, SYS_RES_IRQ,
92250008Sadrian						     0, irq);
93250008Sadrian			return (ENOMEM);
94250008Sadrian		}
95250008Sadrian	} else
96250008Sadrian		drq = 0;
97250008Sadrian
98250008Sadrian	bt_init_softc(dev, port, irq, drq);
99250008Sadrian
100250008Sadrian	return (0);
101250008Sadrian}
102250008Sadrian
103250008Sadrianstatic void
104250008Sadrianbt_isa_release_resources(device_t dev)
105250008Sadrian{
106250008Sadrian	struct	bt_softc *bt = device_get_softc(dev);
107250008Sadrian
108250008Sadrian	if (bt->port)
109250008Sadrian		bus_release_resource(dev, SYS_RES_IOPORT, 0, bt->port);
110250008Sadrian	if (bt->irq)
111250008Sadrian		bus_release_resource(dev, SYS_RES_IRQ, 0, bt->irq);
112250008Sadrian	if (bt->drq)
113250008Sadrian		bus_release_resource(dev, SYS_RES_DRQ, 0, bt->drq);
114250008Sadrian	bt_free_softc(dev);
115250008Sadrian}
116250008Sadrian
117250008Sadrian/*
118250008Sadrian * Check if the device can be found at the port given
119250008Sadrian * and if so, set it up ready for further work
120250008Sadrian * as an argument, takes the isa_device structure from
121250008Sadrian * autoconf.c
122250008Sadrian */
123250008Sadrianstatic int
124250008Sadrianbt_isa_probe(device_t dev)
125250008Sadrian{
126250008Sadrian	/*
127250008Sadrian	 * find unit and check we have that many defined
128250008Sadrian	 */
129250008Sadrian	int	port_index;
130250008Sadrian        int	max_port_index;
131250008Sadrian
132250008Sadrian	/* No pnp support */
133250008Sadrian	if (isa_get_vendorid(dev))
134250008Sadrian		return (ENXIO);
135250008Sadrian
136250008Sadrian	port_index = 0;
137250008Sadrian	max_port_index = BT_NUM_ISAPORTS - 1;
138250008Sadrian	/*
139250008Sadrian	 * Bound our board search if the user has
140250008Sadrian	 * specified an exact port.
141250008Sadrian	 */
142250008Sadrian	bt_find_probe_range(isa_get_port(dev), &port_index, &max_port_index);
143250008Sadrian
144250008Sadrian	if (port_index < 0)
145250008Sadrian		return (ENXIO);
146250008Sadrian
147250008Sadrian	/* Attempt to find an adapter */
148250008Sadrian	for (;port_index <= max_port_index; port_index++) {
149250008Sadrian		struct bt_probe_info info;
150250008Sadrian		u_int ioport;
151250008Sadrian
152250008Sadrian		ioport = bt_iop_from_bio(port_index);
153250008Sadrian
154250008Sadrian		/*
155250008Sadrian		 * Ensure this port has not already been claimed already
156250008Sadrian		 * by a PCI, EISA or ISA adapter.
157250008Sadrian		 */
158250008Sadrian		if (bt_check_probed_iop(ioport) != 0)
159250008Sadrian			continue;
160250008Sadrian
161250008Sadrian		/* Initialise the softc for use during probing */
162250008Sadrian		if (bt_isa_alloc_resources(dev, ioport,
163250008Sadrian					   ioport + BT_NREGS -1) != 0)
164250008Sadrian			continue;
165250008Sadrian
166250008Sadrian		/* We're going to attempt to probe it now, so mark it probed */
167250008Sadrian		bt_mark_probed_bio(port_index);
168250008Sadrian
169250008Sadrian		if (bt_port_probe(dev, &info) != 0) {
170250008Sadrian			if (bootverbose)
171250008Sadrian				printf("bt_isa_probe: Probe failed at 0x%x\n",
172				       ioport);
173			bt_isa_release_resources(dev);
174			continue;
175		}
176
177		bt_isa_release_resources(dev);
178
179		bus_set_resource(dev, SYS_RES_DRQ, 0, info.drq, 1);
180		bus_set_resource(dev, SYS_RES_IRQ, 0, info.irq, 1);
181
182		return (0);
183	}
184
185	return (ENXIO);
186}
187
188/*
189 * Attach all the sub-devices we can find
190 */
191static int
192bt_isa_attach(device_t dev)
193{
194	struct	bt_softc *bt = device_get_softc(dev);
195	bus_dma_filter_t *filter;
196	void		 *filter_arg;
197	bus_addr_t	 lowaddr;
198	int		 error, drq;
199
200	/* Initialise softc */
201	error = bt_isa_alloc_resources(dev, 0, ~0);
202	if (error) {
203		device_printf(dev, "can't allocate resources in bt_isa_attach\n");
204		return error;
205	}
206
207	/* Program the DMA channel for external control */
208	if ((drq = isa_get_drq(dev)) != -1)
209		isa_dmacascade(drq);
210
211	/* Allocate our parent dmatag */
212	filter = NULL;
213	filter_arg = NULL;
214	lowaddr = BUS_SPACE_MAXADDR_24BIT;
215	if (bt->model[0] == '4') {
216		/*
217		 * This is a VL adapter.  Typically, VL devices have access
218		 * to the full 32bit address space.  On BT-445S adapters
219		 * prior to revision E, there is a hardware bug that causes
220		 * corruption of transfers to/from addresses in the range of
221		 * the BIOS modulo 16MB.  The only properly functioning
222		 * BT-445S Host Adapters have firmware version 3.37.
223		 * If we encounter one of these adapters and the BIOS is
224		 * installed, install a filter function for our bus_dma_map
225		 * that will catch these accesses and bounce them to a safe
226		 * region of memory.
227		 */
228		if (bt->bios_addr != 0
229		 && strcmp(bt->model, "445S") == 0
230		 && strcmp(bt->firmware_ver, "3.37") < 0) {
231			filter = btvlbouncefilter;
232			filter_arg = bt;
233		} else {
234			lowaddr = BUS_SPACE_MAXADDR_32BIT;
235		}
236	}
237
238	/* XXX Should be a child of the ISA or VL bus dma tag */
239	if (bus_dma_tag_create(	/* parent	*/ NULL,
240				/* alignemnt	*/ 1,
241				/* boundary	*/ 0,
242				/* lowaddr	*/ lowaddr,
243				/* highaddr	*/ BUS_SPACE_MAXADDR,
244				/* filter	*/ filter,
245				/* filterarg	*/ filter_arg,
246				/* maxsize	*/ BUS_SPACE_MAXSIZE_32BIT,
247				/* nsegments	*/ ~0,
248				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
249				/* flags	*/ 0,
250				/* lockfunc	*/ busdma_lock_mutex,
251				/* lockarg	*/ &Giant,
252				&bt->parent_dmat) != 0) {
253		bt_isa_release_resources(dev);
254                return (ENOMEM);
255        }
256
257        error = bt_init(dev);
258        if (error) {
259		bt_isa_release_resources(dev);
260                return (ENOMEM);
261        }
262
263	if (lowaddr != BUS_SPACE_MAXADDR_32BIT) {
264		/* DMA tag for our sense buffers */
265		if (bus_dma_tag_create(
266				/* parent	*/ bt->parent_dmat,
267				/* alignment	*/ 1,
268				/* boundary	*/ 0,
269				/* lowaddr	*/ BUS_SPACE_MAXADDR,
270				/* highaddr	*/ BUS_SPACE_MAXADDR,
271				/* filter	*/ NULL,
272				/* filterarg	*/ NULL,
273				/* maxsize	*/ bt->max_ccbs *
274						   sizeof(struct scsi_sense_data),
275				/* nsegments	*/ 1,
276				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
277				/* flags	*/ 0,
278				/* lockfunc	*/ busdma_lock_mutex,
279				/* lockarg	*/ &Giant,
280				&bt->sense_dmat) != 0) {
281			bt_isa_release_resources(dev);
282			return (ENOMEM);
283		}
284
285		bt->init_level++;
286
287		/* Allocation of sense buffers */
288		if (bus_dmamem_alloc(bt->sense_dmat,
289				     (void **)&bt->sense_buffers,
290				     BUS_DMA_NOWAIT, &bt->sense_dmamap) != 0) {
291			bt_isa_release_resources(dev);
292			return (ENOMEM);
293		}
294
295		bt->init_level++;
296
297		/* And permanently map them */
298		bus_dmamap_load(bt->sense_dmat, bt->sense_dmamap,
299       				bt->sense_buffers,
300				bt->max_ccbs * sizeof(*bt->sense_buffers),
301				btmapsensebuffers, bt, /*flags*/0);
302
303		bt->init_level++;
304	}
305
306	error = bt_attach(dev);
307	if (error) {
308		bt_isa_release_resources(dev);
309		return (error);
310	}
311
312	return (0);
313}
314
315#define BIOS_MAP_SIZE (16 * 1024)
316
317static int
318btvlbouncefilter(void *arg, bus_addr_t addr)
319{
320	struct bt_softc *bt;
321
322	bt = (struct bt_softc *)arg;
323
324	addr &= BUS_SPACE_MAXADDR_24BIT;
325
326	if (addr == 0
327	 || (addr >= bt->bios_addr
328	  && addr < (bt->bios_addr + BIOS_MAP_SIZE)))
329		return (1);
330	return (0);
331}
332
333static void
334btmapsensebuffers(void *arg, bus_dma_segment_t *segs, int nseg, int error)
335{
336	struct bt_softc* bt;
337
338	bt = (struct bt_softc*)arg;
339	bt->sense_buffers_physbase = segs->ds_addr;
340}
341
342static device_method_t bt_isa_methods[] = {
343	/* Device interface */
344	DEVMETHOD(device_probe,		bt_isa_probe),
345	DEVMETHOD(device_attach,	bt_isa_attach),
346
347	{ 0, 0 }
348};
349
350static driver_t bt_isa_driver = {
351	"bt",
352	bt_isa_methods,
353	sizeof(struct bt_softc),
354};
355
356static devclass_t bt_devclass;
357
358DRIVER_MODULE(bt, isa, bt_isa_driver, bt_devclass, 0, 0);
359