1139749Simp/*-
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 *
2939223Sgibbs */
3039223Sgibbs
31119418Sobrien#include <sys/cdefs.h>
32119418Sobrien__FBSDID("$FreeBSD$");
33119418Sobrien
3439223Sgibbs#include <sys/param.h>
3539223Sgibbs#include <sys/systm.h>
3645791Speter#include <sys/kernel.h>
3745791Speter#include <sys/module.h>
38117126Sscottl#include <sys/lock.h>
39117126Sscottl#include <sys/mutex.h>
4045791Speter#include <sys/bus.h>
4139223Sgibbs
4239223Sgibbs#include <machine/bus.h>
4345791Speter#include <machine/resource.h>
4445791Speter#include <sys/rman.h>
4539223Sgibbs
4645791Speter#include <isa/isavar.h>
4739223Sgibbs#include <dev/buslogic/btreg.h>
4839223Sgibbs
4939223Sgibbs#include <cam/scsi/scsi_all.h>
5039223Sgibbs
5139223Sgibbsstatic	bus_dma_filter_t btvlbouncefilter;
5239223Sgibbsstatic	bus_dmamap_callback_t btmapsensebuffers;
5339223Sgibbs
5445791Speterstatic int
5547399Sdfrbt_isa_alloc_resources(device_t dev, u_long portstart, u_long portend)
5639223Sgibbs{
5745791Speter	int rid;
5845791Speter	struct resource *port;
5945791Speter	struct resource *irq;
6045791Speter	struct resource *drq;
6139223Sgibbs
6245791Speter	rid = 0;
6345791Speter	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
6447399Sdfr				  portstart, portend, BT_NREGS, RF_ACTIVE);
6545791Speter	if (!port)
6645791Speter		return (ENOMEM);
6745791Speter
6845791Speter	if (isa_get_irq(dev) != -1) {
6945791Speter		rid = 0;
70127135Snjl		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
7145791Speter		if (!irq) {
7245791Speter			if (port)
7345791Speter				bus_release_resource(dev, SYS_RES_IOPORT,
7445791Speter						     0, port);
7545791Speter			return (ENOMEM);
7645791Speter		}
7745791Speter	} else
78241592Sjhb		irq = NULL;
7945791Speter
8045791Speter	if (isa_get_drq(dev) != -1) {
8145791Speter		rid = 0;
82127135Snjl		drq = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid, RF_ACTIVE);
8345791Speter		if (!drq) {
8445791Speter			if (port)
8545791Speter				bus_release_resource(dev, SYS_RES_IOPORT,
8645791Speter						     0, port);
8745791Speter			if (irq)
8845791Speter				bus_release_resource(dev, SYS_RES_IRQ,
8945791Speter						     0, irq);
9045791Speter			return (ENOMEM);
9145791Speter		}
9245791Speter	} else
93241592Sjhb		drq = NULL;
9445791Speter
9545791Speter	bt_init_softc(dev, port, irq, drq);
9645791Speter
9745791Speter	return (0);
9845791Speter}
9945791Speter
10045791Speterstatic void
10145791Speterbt_isa_release_resources(device_t dev)
10245791Speter{
10345791Speter	struct	bt_softc *bt = device_get_softc(dev);
10445791Speter
10545791Speter	if (bt->port)
10645791Speter		bus_release_resource(dev, SYS_RES_IOPORT, 0, bt->port);
10745791Speter	if (bt->irq)
10845796Speter		bus_release_resource(dev, SYS_RES_IRQ, 0, bt->irq);
10945791Speter	if (bt->drq)
11045796Speter		bus_release_resource(dev, SYS_RES_DRQ, 0, bt->drq);
11145791Speter	bt_free_softc(dev);
11245791Speter}
11345791Speter
11439223Sgibbs/*
11539223Sgibbs * Check if the device can be found at the port given
11639223Sgibbs * and if so, set it up ready for further work
11739223Sgibbs * as an argument, takes the isa_device structure from
11839223Sgibbs * autoconf.c
11939223Sgibbs */
12039223Sgibbsstatic int
12145791Speterbt_isa_probe(device_t dev)
12239223Sgibbs{
12339223Sgibbs	/*
12439223Sgibbs	 * find unit and check we have that many defined
12539223Sgibbs	 */
12639223Sgibbs	int	port_index;
12739223Sgibbs        int	max_port_index;
12839223Sgibbs
12947617Sdfr	/* No pnp support */
13047617Sdfr	if (isa_get_vendorid(dev))
13147617Sdfr		return (ENXIO);
13247617Sdfr
13339223Sgibbs	port_index = 0;
13439223Sgibbs	max_port_index = BT_NUM_ISAPORTS - 1;
13539223Sgibbs	/*
13639223Sgibbs	 * Bound our board search if the user has
13739223Sgibbs	 * specified an exact port.
13839223Sgibbs	 */
13945791Speter	bt_find_probe_range(isa_get_port(dev), &port_index, &max_port_index);
14039223Sgibbs
14141048Sgibbs	if (port_index < 0)
14245791Speter		return (ENXIO);
14341048Sgibbs
14439223Sgibbs	/* Attempt to find an adapter */
14539223Sgibbs	for (;port_index <= max_port_index; port_index++) {
14644579Sgibbs		struct bt_probe_info info;
14739223Sgibbs		u_int ioport;
14839223Sgibbs
14941048Sgibbs		ioport = bt_iop_from_bio(port_index);
15039223Sgibbs
15139223Sgibbs		/*
15239223Sgibbs		 * Ensure this port has not already been claimed already
15340265Simp		 * by a PCI, EISA or ISA adapter.
15439223Sgibbs		 */
15539223Sgibbs		if (bt_check_probed_iop(ioport) != 0)
15639223Sgibbs			continue;
15745791Speter
15845791Speter		/* Initialise the softc for use during probing */
15947399Sdfr		if (bt_isa_alloc_resources(dev, ioport,
16047399Sdfr					   ioport + BT_NREGS -1) != 0)
16140160Simp			continue;
16240160Simp
16339223Sgibbs		/* We're going to attempt to probe it now, so mark it probed */
16439223Sgibbs		bt_mark_probed_bio(port_index);
16539223Sgibbs
16645791Speter		if (bt_port_probe(dev, &info) != 0) {
16747717Speter			if (bootverbose)
16847717Speter				printf("bt_isa_probe: Probe failed at 0x%x\n",
16947717Speter				       ioport);
17045791Speter			bt_isa_release_resources(dev);
17139223Sgibbs			continue;
17239223Sgibbs		}
17339223Sgibbs
17445791Speter		bt_isa_release_resources(dev);
17540160Simp
17652174Sdfr		bus_set_resource(dev, SYS_RES_DRQ, 0, info.drq, 1);
17752174Sdfr		bus_set_resource(dev, SYS_RES_IRQ, 0, info.irq, 1);
17845791Speter
179241592Sjhb		return (BUS_PROBE_DEFAULT);
18039223Sgibbs	}
18139223Sgibbs
18245791Speter	return (ENXIO);
18339223Sgibbs}
18439223Sgibbs
18539223Sgibbs/*
18639223Sgibbs * Attach all the sub-devices we can find
18739223Sgibbs */
18839223Sgibbsstatic int
18945791Speterbt_isa_attach(device_t dev)
19039223Sgibbs{
19145791Speter	struct	bt_softc *bt = device_get_softc(dev);
19239223Sgibbs	bus_dma_filter_t *filter;
19339223Sgibbs	void		 *filter_arg;
19439223Sgibbs	bus_addr_t	 lowaddr;
19545796Speter	int		 error, drq;
19639223Sgibbs
19745791Speter	/* Initialise softc */
19847399Sdfr	error = bt_isa_alloc_resources(dev, 0, ~0);
19945791Speter	if (error) {
20045791Speter		device_printf(dev, "can't allocate resources in bt_isa_attach\n");
20145791Speter		return error;
20245791Speter	}
20339223Sgibbs
20445796Speter	/* Program the DMA channel for external control */
20545796Speter	if ((drq = isa_get_drq(dev)) != -1)
20645796Speter		isa_dmacascade(drq);
20745796Speter
20839223Sgibbs	/* Allocate our parent dmatag */
20939223Sgibbs	filter = NULL;
21039223Sgibbs	filter_arg = NULL;
21139223Sgibbs	lowaddr = BUS_SPACE_MAXADDR_24BIT;
21239223Sgibbs	if (bt->model[0] == '4') {
21339223Sgibbs		/*
21439223Sgibbs		 * This is a VL adapter.  Typically, VL devices have access
21539223Sgibbs		 * to the full 32bit address space.  On BT-445S adapters
21639223Sgibbs		 * prior to revision E, there is a hardware bug that causes
21739223Sgibbs		 * corruption of transfers to/from addresses in the range of
21839223Sgibbs		 * the BIOS modulo 16MB.  The only properly functioning
21939223Sgibbs		 * BT-445S Host Adapters have firmware version 3.37.
22039223Sgibbs		 * If we encounter one of these adapters and the BIOS is
22139223Sgibbs		 * installed, install a filter function for our bus_dma_map
22239223Sgibbs		 * that will catch these accesses and bounce them to a safe
22339223Sgibbs		 * region of memory.
22439223Sgibbs		 */
22539223Sgibbs		if (bt->bios_addr != 0
22639223Sgibbs		 && strcmp(bt->model, "445S") == 0
22739223Sgibbs		 && strcmp(bt->firmware_ver, "3.37") < 0) {
22839223Sgibbs			filter = btvlbouncefilter;
22939223Sgibbs			filter_arg = bt;
23039223Sgibbs		} else {
23139223Sgibbs			lowaddr = BUS_SPACE_MAXADDR_32BIT;
23239223Sgibbs		}
23339223Sgibbs	}
23439223Sgibbs
23539223Sgibbs	/* XXX Should be a child of the ISA or VL bus dma tag */
236241592Sjhb	if (bus_dma_tag_create(	/* parent	*/ bus_get_dma_tag(dev),
237112782Smdodd				/* alignemnt	*/ 1,
238112782Smdodd				/* boundary	*/ 0,
239112782Smdodd				/* lowaddr	*/ lowaddr,
240112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
241112782Smdodd				/* filter	*/ filter,
242112782Smdodd				/* filterarg	*/ filter_arg,
243112782Smdodd				/* maxsize	*/ BUS_SPACE_MAXSIZE_32BIT,
244112782Smdodd				/* nsegments	*/ ~0,
245112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
246112782Smdodd				/* flags	*/ 0,
247241592Sjhb				/* lockfunc	*/ NULL,
248241592Sjhb				/* lockarg	*/ NULL,
249112782Smdodd				&bt->parent_dmat) != 0) {
25045791Speter		bt_isa_release_resources(dev);
25145791Speter                return (ENOMEM);
25239223Sgibbs        }
25339223Sgibbs
25445796Speter        error = bt_init(dev);
25545796Speter        if (error) {
25645791Speter		bt_isa_release_resources(dev);
25745791Speter                return (ENOMEM);
25839223Sgibbs        }
25939223Sgibbs
26039223Sgibbs	if (lowaddr != BUS_SPACE_MAXADDR_32BIT) {
26139223Sgibbs		/* DMA tag for our sense buffers */
262112782Smdodd		if (bus_dma_tag_create(
263112782Smdodd				/* parent	*/ bt->parent_dmat,
264112782Smdodd				/* alignment	*/ 1,
265112782Smdodd				/* boundary	*/ 0,
266112782Smdodd				/* lowaddr	*/ BUS_SPACE_MAXADDR,
267112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
268112782Smdodd				/* filter	*/ NULL,
269112782Smdodd				/* filterarg	*/ NULL,
270112782Smdodd				/* maxsize	*/ bt->max_ccbs *
271112782Smdodd						   sizeof(struct scsi_sense_data),
272112782Smdodd				/* nsegments	*/ 1,
273112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
274112782Smdodd				/* flags	*/ 0,
275241592Sjhb				/* lockfunc	*/ NULL,
276241592Sjhb				/* lockarg	*/ NULL,
277112782Smdodd				&bt->sense_dmat) != 0) {
27845791Speter			bt_isa_release_resources(dev);
27945791Speter			return (ENOMEM);
28039223Sgibbs		}
28139223Sgibbs
28239223Sgibbs		bt->init_level++;
28339223Sgibbs
28439223Sgibbs		/* Allocation of sense buffers */
28539223Sgibbs		if (bus_dmamem_alloc(bt->sense_dmat,
28639223Sgibbs				     (void **)&bt->sense_buffers,
28739223Sgibbs				     BUS_DMA_NOWAIT, &bt->sense_dmamap) != 0) {
28845791Speter			bt_isa_release_resources(dev);
28945791Speter			return (ENOMEM);
29039223Sgibbs		}
29139223Sgibbs
29239223Sgibbs		bt->init_level++;
29339223Sgibbs
29439223Sgibbs		/* And permanently map them */
29539223Sgibbs		bus_dmamap_load(bt->sense_dmat, bt->sense_dmamap,
29639223Sgibbs       				bt->sense_buffers,
29739223Sgibbs				bt->max_ccbs * sizeof(*bt->sense_buffers),
29839223Sgibbs				btmapsensebuffers, bt, /*flags*/0);
29939223Sgibbs
30039223Sgibbs		bt->init_level++;
30139223Sgibbs	}
30239223Sgibbs
30345791Speter	error = bt_attach(dev);
30445791Speter	if (error) {
30545791Speter		bt_isa_release_resources(dev);
30645791Speter		return (error);
30745791Speter	}
30839223Sgibbs
30945791Speter	return (0);
31039223Sgibbs}
31139223Sgibbs
31239223Sgibbs#define BIOS_MAP_SIZE (16 * 1024)
31339223Sgibbs
31439223Sgibbsstatic int
31539223Sgibbsbtvlbouncefilter(void *arg, bus_addr_t addr)
31639223Sgibbs{
31739223Sgibbs	struct bt_softc *bt;
31839223Sgibbs
31939223Sgibbs	bt = (struct bt_softc *)arg;
32039223Sgibbs
32139223Sgibbs	addr &= BUS_SPACE_MAXADDR_24BIT;
32239223Sgibbs
32339223Sgibbs	if (addr == 0
32439223Sgibbs	 || (addr >= bt->bios_addr
32539223Sgibbs	  && addr < (bt->bios_addr + BIOS_MAP_SIZE)))
32639223Sgibbs		return (1);
32739223Sgibbs	return (0);
32839223Sgibbs}
32939223Sgibbs
33039223Sgibbsstatic void
33139223Sgibbsbtmapsensebuffers(void *arg, bus_dma_segment_t *segs, int nseg, int error)
33239223Sgibbs{
33339223Sgibbs	struct bt_softc* bt;
33439223Sgibbs
33539223Sgibbs	bt = (struct bt_softc*)arg;
33639223Sgibbs	bt->sense_buffers_physbase = segs->ds_addr;
33739223Sgibbs}
33845791Speter
33945791Speterstatic device_method_t bt_isa_methods[] = {
34045791Speter	/* Device interface */
34145791Speter	DEVMETHOD(device_probe,		bt_isa_probe),
34245791Speter	DEVMETHOD(device_attach,	bt_isa_attach),
34345791Speter
34445791Speter	{ 0, 0 }
34545791Speter};
34645791Speter
34745791Speterstatic driver_t bt_isa_driver = {
34845791Speter	"bt",
34945791Speter	bt_isa_methods,
35045791Speter	sizeof(struct bt_softc),
35145791Speter};
35245791Speter
35345791Speterstatic devclass_t bt_devclass;
35445791Speter
35545791SpeterDRIVER_MODULE(bt, isa, bt_isa_driver, bt_devclass, 0, 0);
356165102SmjacobMODULE_DEPEND(bt, isa, 1, 1, 1);
357