bt_isa.c revision 112782
139223Sgibbs/*
239223Sgibbs * Product specific probe and attach routines for:
339223Sgibbs *      Buslogic BT-54X and BT-445 cards
439223Sgibbs *
544579Sgibbs * Copyright (c) 1998, 1999 Justin T. Gibbs
639223Sgibbs * All rights reserved.
739223Sgibbs *
839223Sgibbs * Redistribution and use in source and binary forms, with or without
939223Sgibbs * modification, are permitted provided that the following conditions
1039223Sgibbs * are met:
1139223Sgibbs * 1. Redistributions of source code must retain the above copyright
1239223Sgibbs *    notice, this list of conditions, and the following disclaimer,
1339223Sgibbs *    without modification, immediately at the beginning of the file.
1439223Sgibbs * 2. The name of the author may not be used to endorse or promote products
1539223Sgibbs *    derived from this software without specific prior written permission.
1639223Sgibbs *
1739223Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1839223Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1939223Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2039223Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2139223Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2239223Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2339223Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2439223Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2539223Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2639223Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2739223Sgibbs * SUCH DAMAGE.
2839223Sgibbs *
2950477Speter * $FreeBSD: head/sys/dev/buslogic/bt_isa.c 112782 2003-03-29 09:46:10Z mdodd $
3039223Sgibbs */
3139223Sgibbs
3239223Sgibbs#include <sys/param.h>
3339223Sgibbs#include <sys/systm.h>
3445791Speter#include <sys/kernel.h>
3545791Speter#include <sys/module.h>
3645791Speter#include <sys/bus.h>
3739223Sgibbs
3839223Sgibbs#include <machine/bus_pio.h>
3939223Sgibbs#include <machine/bus.h>
4045791Speter#include <machine/resource.h>
4145791Speter#include <sys/rman.h>
4239223Sgibbs
4345791Speter#include <isa/isavar.h>
4439223Sgibbs#include <dev/buslogic/btreg.h>
4539223Sgibbs
4639223Sgibbs#include <cam/scsi/scsi_all.h>
4739223Sgibbs
4839223Sgibbsstatic	bus_dma_filter_t btvlbouncefilter;
4939223Sgibbsstatic	bus_dmamap_callback_t btmapsensebuffers;
5039223Sgibbs
5145791Speterstatic int
5247399Sdfrbt_isa_alloc_resources(device_t dev, u_long portstart, u_long portend)
5339223Sgibbs{
5445791Speter	int rid;
5545791Speter	struct resource *port;
5645791Speter	struct resource *irq;
5745791Speter	struct resource *drq;
5839223Sgibbs
5945791Speter	rid = 0;
6045791Speter	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
6147399Sdfr				  portstart, portend, BT_NREGS, RF_ACTIVE);
6245791Speter	if (!port)
6345791Speter		return (ENOMEM);
6445791Speter
6545791Speter	if (isa_get_irq(dev) != -1) {
6645791Speter		rid = 0;
6745791Speter		irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
6845791Speter					 0, ~0, 1, RF_ACTIVE);
6945791Speter		if (!irq) {
7045791Speter			if (port)
7145791Speter				bus_release_resource(dev, SYS_RES_IOPORT,
7245791Speter						     0, port);
7345791Speter			return (ENOMEM);
7445791Speter		}
7545791Speter	} else
7645791Speter		irq = 0;
7745791Speter
7845791Speter	if (isa_get_drq(dev) != -1) {
7945791Speter		rid = 0;
8045791Speter		drq = bus_alloc_resource(dev, SYS_RES_DRQ, &rid,
8145791Speter					 0, ~0, 1, RF_ACTIVE);
8245791Speter		if (!drq) {
8345791Speter			if (port)
8445791Speter				bus_release_resource(dev, SYS_RES_IOPORT,
8545791Speter						     0, port);
8645791Speter			if (irq)
8745791Speter				bus_release_resource(dev, SYS_RES_IRQ,
8845791Speter						     0, irq);
8945791Speter			return (ENOMEM);
9045791Speter		}
9145791Speter	} else
9245791Speter		drq = 0;
9345791Speter
9445791Speter	bt_init_softc(dev, port, irq, drq);
9545791Speter
9645791Speter	return (0);
9745791Speter}
9845791Speter
9945791Speterstatic void
10045791Speterbt_isa_release_resources(device_t dev)
10145791Speter{
10245791Speter	struct	bt_softc *bt = device_get_softc(dev);
10345791Speter
10445791Speter	if (bt->port)
10545791Speter		bus_release_resource(dev, SYS_RES_IOPORT, 0, bt->port);
10645791Speter	if (bt->irq)
10745796Speter		bus_release_resource(dev, SYS_RES_IRQ, 0, bt->irq);
10845791Speter	if (bt->drq)
10945796Speter		bus_release_resource(dev, SYS_RES_DRQ, 0, bt->drq);
11045791Speter	bt_free_softc(dev);
11145791Speter}
11245791Speter
11339223Sgibbs/*
11439223Sgibbs * Check if the device can be found at the port given
11539223Sgibbs * and if so, set it up ready for further work
11639223Sgibbs * as an argument, takes the isa_device structure from
11739223Sgibbs * autoconf.c
11839223Sgibbs */
11939223Sgibbsstatic int
12045791Speterbt_isa_probe(device_t dev)
12139223Sgibbs{
12239223Sgibbs	/*
12339223Sgibbs	 * find unit and check we have that many defined
12439223Sgibbs	 */
12539223Sgibbs	int	port_index;
12639223Sgibbs        int	max_port_index;
12739223Sgibbs
12847617Sdfr	/* No pnp support */
12947617Sdfr	if (isa_get_vendorid(dev))
13047617Sdfr		return (ENXIO);
13147617Sdfr
13239223Sgibbs	port_index = 0;
13339223Sgibbs	max_port_index = BT_NUM_ISAPORTS - 1;
13439223Sgibbs	/*
13539223Sgibbs	 * Bound our board search if the user has
13639223Sgibbs	 * specified an exact port.
13739223Sgibbs	 */
13845791Speter	bt_find_probe_range(isa_get_port(dev), &port_index, &max_port_index);
13939223Sgibbs
14041048Sgibbs	if (port_index < 0)
14145791Speter		return (ENXIO);
14241048Sgibbs
14339223Sgibbs	/* Attempt to find an adapter */
14439223Sgibbs	for (;port_index <= max_port_index; port_index++) {
14544579Sgibbs		struct bt_probe_info info;
14639223Sgibbs		u_int ioport;
14739223Sgibbs
14841048Sgibbs		ioport = bt_iop_from_bio(port_index);
14939223Sgibbs
15039223Sgibbs		/*
15139223Sgibbs		 * Ensure this port has not already been claimed already
15240265Simp		 * by a PCI, EISA or ISA adapter.
15339223Sgibbs		 */
15439223Sgibbs		if (bt_check_probed_iop(ioport) != 0)
15539223Sgibbs			continue;
15645791Speter
15745791Speter		/* Initialise the softc for use during probing */
15847399Sdfr		if (bt_isa_alloc_resources(dev, ioport,
15947399Sdfr					   ioport + BT_NREGS -1) != 0)
16040160Simp			continue;
16140160Simp
16239223Sgibbs		/* We're going to attempt to probe it now, so mark it probed */
16339223Sgibbs		bt_mark_probed_bio(port_index);
16439223Sgibbs
16545791Speter		if (bt_port_probe(dev, &info) != 0) {
16647717Speter			if (bootverbose)
16747717Speter				printf("bt_isa_probe: Probe failed at 0x%x\n",
16847717Speter				       ioport);
16945791Speter			bt_isa_release_resources(dev);
17039223Sgibbs			continue;
17139223Sgibbs		}
17239223Sgibbs
17345791Speter		bt_isa_release_resources(dev);
17440160Simp
17552174Sdfr		bus_set_resource(dev, SYS_RES_DRQ, 0, info.drq, 1);
17652174Sdfr		bus_set_resource(dev, SYS_RES_IRQ, 0, info.irq, 1);
17745791Speter
17845791Speter		return (0);
17939223Sgibbs	}
18039223Sgibbs
18145791Speter	return (ENXIO);
18239223Sgibbs}
18339223Sgibbs
18439223Sgibbs/*
18539223Sgibbs * Attach all the sub-devices we can find
18639223Sgibbs */
18739223Sgibbsstatic int
18845791Speterbt_isa_attach(device_t dev)
18939223Sgibbs{
19045791Speter	struct	bt_softc *bt = device_get_softc(dev);
19139223Sgibbs	bus_dma_filter_t *filter;
19239223Sgibbs	void		 *filter_arg;
19339223Sgibbs	bus_addr_t	 lowaddr;
19445796Speter	int		 error, drq;
19539223Sgibbs
19645791Speter	/* Initialise softc */
19747399Sdfr	error = bt_isa_alloc_resources(dev, 0, ~0);
19845791Speter	if (error) {
19945791Speter		device_printf(dev, "can't allocate resources in bt_isa_attach\n");
20045791Speter		return error;
20145791Speter	}
20239223Sgibbs
20345796Speter	/* Program the DMA channel for external control */
20445796Speter	if ((drq = isa_get_drq(dev)) != -1)
20545796Speter		isa_dmacascade(drq);
20645796Speter
20739223Sgibbs	/* Allocate our parent dmatag */
20839223Sgibbs	filter = NULL;
20939223Sgibbs	filter_arg = NULL;
21039223Sgibbs	lowaddr = BUS_SPACE_MAXADDR_24BIT;
21139223Sgibbs	if (bt->model[0] == '4') {
21239223Sgibbs		/*
21339223Sgibbs		 * This is a VL adapter.  Typically, VL devices have access
21439223Sgibbs		 * to the full 32bit address space.  On BT-445S adapters
21539223Sgibbs		 * prior to revision E, there is a hardware bug that causes
21639223Sgibbs		 * corruption of transfers to/from addresses in the range of
21739223Sgibbs		 * the BIOS modulo 16MB.  The only properly functioning
21839223Sgibbs		 * BT-445S Host Adapters have firmware version 3.37.
21939223Sgibbs		 * If we encounter one of these adapters and the BIOS is
22039223Sgibbs		 * installed, install a filter function for our bus_dma_map
22139223Sgibbs		 * that will catch these accesses and bounce them to a safe
22239223Sgibbs		 * region of memory.
22339223Sgibbs		 */
22439223Sgibbs		if (bt->bios_addr != 0
22539223Sgibbs		 && strcmp(bt->model, "445S") == 0
22639223Sgibbs		 && strcmp(bt->firmware_ver, "3.37") < 0) {
22739223Sgibbs			filter = btvlbouncefilter;
22839223Sgibbs			filter_arg = bt;
22939223Sgibbs		} else {
23039223Sgibbs			lowaddr = BUS_SPACE_MAXADDR_32BIT;
23139223Sgibbs		}
23239223Sgibbs	}
23339223Sgibbs
23439223Sgibbs	/* XXX Should be a child of the ISA or VL bus dma tag */
235112782Smdodd	if (bus_dma_tag_create(	/* parent	*/ NULL,
236112782Smdodd				/* alignemnt	*/ 1,
237112782Smdodd				/* boundary	*/ 0,
238112782Smdodd				/* lowaddr	*/ lowaddr,
239112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
240112782Smdodd				/* filter	*/ filter,
241112782Smdodd				/* filterarg	*/ filter_arg,
242112782Smdodd				/* maxsize	*/ BUS_SPACE_MAXSIZE_32BIT,
243112782Smdodd				/* nsegments	*/ ~0,
244112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
245112782Smdodd				/* flags	*/ 0,
246112782Smdodd				&bt->parent_dmat) != 0) {
24745791Speter		bt_isa_release_resources(dev);
24845791Speter                return (ENOMEM);
24939223Sgibbs        }
25039223Sgibbs
25145796Speter        error = bt_init(dev);
25245796Speter        if (error) {
25345791Speter		bt_isa_release_resources(dev);
25445791Speter                return (ENOMEM);
25539223Sgibbs        }
25639223Sgibbs
25739223Sgibbs	if (lowaddr != BUS_SPACE_MAXADDR_32BIT) {
25839223Sgibbs		/* DMA tag for our sense buffers */
259112782Smdodd		if (bus_dma_tag_create(
260112782Smdodd				/* parent	*/ bt->parent_dmat,
261112782Smdodd				/* alignment	*/ 1,
262112782Smdodd				/* boundary	*/ 0,
263112782Smdodd				/* lowaddr	*/ BUS_SPACE_MAXADDR,
264112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
265112782Smdodd				/* filter	*/ NULL,
266112782Smdodd				/* filterarg	*/ NULL,
267112782Smdodd				/* maxsize	*/ bt->max_ccbs *
268112782Smdodd						   sizeof(struct scsi_sense_data),
269112782Smdodd				/* nsegments	*/ 1,
270112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
271112782Smdodd				/* flags	*/ 0,
272112782Smdodd				&bt->sense_dmat) != 0) {
27345791Speter			bt_isa_release_resources(dev);
27445791Speter			return (ENOMEM);
27539223Sgibbs		}
27639223Sgibbs
27739223Sgibbs		bt->init_level++;
27839223Sgibbs
27939223Sgibbs		/* Allocation of sense buffers */
28039223Sgibbs		if (bus_dmamem_alloc(bt->sense_dmat,
28139223Sgibbs				     (void **)&bt->sense_buffers,
28239223Sgibbs				     BUS_DMA_NOWAIT, &bt->sense_dmamap) != 0) {
28345791Speter			bt_isa_release_resources(dev);
28445791Speter			return (ENOMEM);
28539223Sgibbs		}
28639223Sgibbs
28739223Sgibbs		bt->init_level++;
28839223Sgibbs
28939223Sgibbs		/* And permanently map them */
29039223Sgibbs		bus_dmamap_load(bt->sense_dmat, bt->sense_dmamap,
29139223Sgibbs       				bt->sense_buffers,
29239223Sgibbs				bt->max_ccbs * sizeof(*bt->sense_buffers),
29339223Sgibbs				btmapsensebuffers, bt, /*flags*/0);
29439223Sgibbs
29539223Sgibbs		bt->init_level++;
29639223Sgibbs	}
29739223Sgibbs
29845791Speter	error = bt_attach(dev);
29945791Speter	if (error) {
30045791Speter		bt_isa_release_resources(dev);
30145791Speter		return (error);
30245791Speter	}
30339223Sgibbs
30445791Speter	return (0);
30539223Sgibbs}
30639223Sgibbs
30739223Sgibbs#define BIOS_MAP_SIZE (16 * 1024)
30839223Sgibbs
30939223Sgibbsstatic int
31039223Sgibbsbtvlbouncefilter(void *arg, bus_addr_t addr)
31139223Sgibbs{
31239223Sgibbs	struct bt_softc *bt;
31339223Sgibbs
31439223Sgibbs	bt = (struct bt_softc *)arg;
31539223Sgibbs
31639223Sgibbs	addr &= BUS_SPACE_MAXADDR_24BIT;
31739223Sgibbs
31839223Sgibbs	if (addr == 0
31939223Sgibbs	 || (addr >= bt->bios_addr
32039223Sgibbs	  && addr < (bt->bios_addr + BIOS_MAP_SIZE)))
32139223Sgibbs		return (1);
32239223Sgibbs	return (0);
32339223Sgibbs}
32439223Sgibbs
32539223Sgibbsstatic void
32639223Sgibbsbtmapsensebuffers(void *arg, bus_dma_segment_t *segs, int nseg, int error)
32739223Sgibbs{
32839223Sgibbs	struct bt_softc* bt;
32939223Sgibbs
33039223Sgibbs	bt = (struct bt_softc*)arg;
33139223Sgibbs	bt->sense_buffers_physbase = segs->ds_addr;
33239223Sgibbs}
33345791Speter
33445791Speterstatic device_method_t bt_isa_methods[] = {
33545791Speter	/* Device interface */
33645791Speter	DEVMETHOD(device_probe,		bt_isa_probe),
33745791Speter	DEVMETHOD(device_attach,	bt_isa_attach),
33845791Speter
33945791Speter	{ 0, 0 }
34045791Speter};
34145791Speter
34245791Speterstatic driver_t bt_isa_driver = {
34345791Speter	"bt",
34445791Speter	bt_isa_methods,
34545791Speter	sizeof(struct bt_softc),
34645791Speter};
34745791Speter
34845791Speterstatic devclass_t bt_devclass;
34945791Speter
35045791SpeterDRIVER_MODULE(bt, isa, bt_isa_driver, bt_devclass, 0, 0);
351