adv_isa.c revision 112782
118781Sgibbs/*
218781Sgibbs * Device probe and attach routines for the following
318781Sgibbs * Advanced Systems Inc. SCSI controllers:
418781Sgibbs *
539217Sgibbs *   Connectivity Products:
639217Sgibbs *	ABP510/5150 - Bus-Master ISA (240 CDB) *
739217Sgibbs *	ABP5140 - Bus-Master ISA PnP (16 CDB) * **
839217Sgibbs *	ABP5142 - Bus-Master ISA PnP with floppy (16 CDB) ***
918781Sgibbs *
1039217Sgibbs *   Single Channel Products:
1139217Sgibbs *	ABP542 - Bus-Master ISA with floppy (240 CDB)
1239217Sgibbs *	ABP842 - Bus-Master VL (240 CDB)
1318781Sgibbs *
1439217Sgibbs *   Dual Channel Products:
1539217Sgibbs *	ABP852 - Dual Channel Bus-Master VL (240 CDB Per Channel)
1618781Sgibbs *
1739217Sgibbs *    * This board has been shipped by HP with the 4020i CD-R drive.
1839217Sgibbs *      The board has no BIOS so it cannot control a boot device, but
1939217Sgibbs *      it can control any secondary SCSI device.
2039217Sgibbs *   ** This board has been sold by SIIG as the i540 SpeedMaster.
2139217Sgibbs *  *** This board has been sold by SIIG as the i542 SpeedMaster.
2239217Sgibbs *
2339217Sgibbs * Copyright (c) 1996, 1997 Justin T. Gibbs.
2418781Sgibbs * All rights reserved.
2518781Sgibbs *
2618781Sgibbs * Redistribution and use in source and binary forms, with or without
2718781Sgibbs * modification, are permitted provided that the following conditions
2818781Sgibbs * are met:
2918781Sgibbs * 1. Redistributions of source code must retain the above copyright
3039217Sgibbs *    notice, this list of conditions, and the following disclaimer,
3139217Sgibbs *    without modification, immediately at the beginning of the file.
3239217Sgibbs * 2. The name of the author may not be used to endorse or promote products
3318781Sgibbs *    derived from this software without specific prior written permission.
3418781Sgibbs *
3518781Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
3618781Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3718781Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3818781Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
3918781Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4018781Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4118781Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4218781Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4318781Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4418781Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4518781Sgibbs * SUCH DAMAGE.
4618781Sgibbs *
4750477Speter * $FreeBSD: head/sys/dev/advansys/adv_isa.c 112782 2003-03-29 09:46:10Z mdodd $
4818781Sgibbs */
4918781Sgibbs
5018781Sgibbs#include <sys/param.h>
5118781Sgibbs#include <sys/systm.h>
5259082Snyan#include <sys/kernel.h>
5318781Sgibbs
5439217Sgibbs#include <machine/bus_pio.h>
5539217Sgibbs#include <machine/bus.h>
5659082Snyan#include <machine/resource.h>
5759082Snyan#include <sys/bus.h>
5859082Snyan#include <sys/rman.h>
5939217Sgibbs
6059082Snyan#include <isa/isavar.h>
6118781Sgibbs
6239217Sgibbs#include <dev/advansys/advansys.h>
6318781Sgibbs
6439217Sgibbs#include <cam/scsi/scsi_all.h>
6539217Sgibbs
6618781Sgibbs#define ADV_ISA_MAX_DMA_ADDR    (0x00FFFFFFL)
6718781Sgibbs#define ADV_ISA_MAX_DMA_COUNT   (0x00FFFFFFL)
6818781Sgibbs
6918781Sgibbs#define ADV_VL_MAX_DMA_ADDR     (0x07FFFFFFL)
7018781Sgibbs#define ADV_VL_MAX_DMA_COUNT    (0x07FFFFFFL)
7118781Sgibbs
7239217Sgibbs/*
7339217Sgibbs * The overrun buffer shared amongst all ISA/VL adapters.
7439217Sgibbs */
7539217Sgibbsstatic	u_int8_t*	overrun_buf;
7642012Sgibbsstatic	bus_dma_tag_t	overrun_dmat;
7742012Sgibbsstatic	bus_dmamap_t	overrun_dmamap;
7842012Sgibbsstatic	bus_addr_t	overrun_physbase;
7939217Sgibbs
8018781Sgibbs/* Possible port addresses an ISA or VL adapter can live at */
8145577Seivindstatic u_int16_t adv_isa_ioports[] =
8218781Sgibbs{
8318781Sgibbs	0x100,
8418781Sgibbs	0x110,	/* First selection in BIOS setup */
8518781Sgibbs	0x120,
8618781Sgibbs	0x130,	/* Second selection in BIOS setup */
8718781Sgibbs	0x140,
8818781Sgibbs	0x150,	/* Third selection in BIOS setup */
8918781Sgibbs	0x190,	/* Fourth selection in BIOS setup */
9018781Sgibbs	0x210,	/* Fifth selection in BIOS setup */
9118781Sgibbs	0x230,	/* Sixth selection in BIOS setup */
9218781Sgibbs	0x250,	/* Seventh selection in BIOS setup */
9318781Sgibbs	0x330 	/* Eighth and default selection in BIOS setup */
9418781Sgibbs};
9518781Sgibbs
9639217Sgibbs#define MAX_ISA_IOPORT_INDEX (sizeof(adv_isa_ioports)/sizeof(u_int16_t) - 1)
9718781Sgibbs
9859082Snyanstatic	int	adv_isa_probe(device_t dev);
9959082Snyanstatic  int	adv_isa_attach(device_t dev);
10039217Sgibbsstatic	void	adv_set_isapnp_wait_for_key(void);
10139217Sgibbsstatic	int	adv_get_isa_dma_channel(struct adv_softc *adv);
10239217Sgibbsstatic	int	adv_set_isa_dma_settings(struct adv_softc *adv);
10318781Sgibbs
10418781Sgibbsstatic int
10559082Snyanadv_isa_probe(device_t dev)
10618781Sgibbs{
10718781Sgibbs	int	port_index;
10818781Sgibbs	int	max_port_index;
10976309Snyan	u_long	iobase, iocount, irq;
11076309Snyan	int	user_iobase = 0;
11159082Snyan	int	rid = 0;
11259082Snyan	void	*ih;
11359082Snyan	struct resource	*iores, *irqres;
11418781Sgibbs
11518781Sgibbs	/*
11618781Sgibbs	 * Default to scanning all possible device locations.
11718781Sgibbs	 */
11818781Sgibbs	port_index = 0;
11918781Sgibbs	max_port_index = MAX_ISA_IOPORT_INDEX;
12018781Sgibbs
12176309Snyan	if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, &iocount) == 0) {
12276309Snyan		user_iobase = 1;
12318781Sgibbs		for (;port_index <= max_port_index; port_index++)
12459082Snyan			if (iobase <= adv_isa_ioports[port_index])
12518781Sgibbs				break;
12618781Sgibbs		if ((port_index > max_port_index)
12759082Snyan		 || (iobase != adv_isa_ioports[port_index])) {
12859534Sphk			if (bootverbose)
12959534Sphk			    printf("adv%d: Invalid baseport of 0x%lx specified. "
13059534Sphk				"Nearest valid baseport is 0x%x.  Failing "
13159082Snyan				"probe.\n", device_get_unit(dev), iobase,
13218781Sgibbs				(port_index <= max_port_index) ?
13318781Sgibbs					adv_isa_ioports[port_index] :
13418781Sgibbs					adv_isa_ioports[max_port_index]);
13559082Snyan			return ENXIO;
13618781Sgibbs		}
13718781Sgibbs		max_port_index = port_index;
13818781Sgibbs	}
13918781Sgibbs
14018781Sgibbs	/* Perform the actual probing */
14118781Sgibbs	adv_set_isapnp_wait_for_key();
14218781Sgibbs	for (;port_index <= max_port_index; port_index++) {
14318781Sgibbs		u_int16_t port_addr = adv_isa_ioports[port_index];
14439217Sgibbs		bus_size_t maxsegsz;
14539217Sgibbs		bus_size_t maxsize;
14639217Sgibbs		bus_addr_t lowaddr;
14739217Sgibbs		int error;
14859171Snyan		struct adv_softc *adv;
14939217Sgibbs
15018781Sgibbs		if (port_addr == 0)
15118781Sgibbs			/* Already been attached */
15218781Sgibbs			continue;
15359082Snyan
15459082Snyan		if (bus_set_resource(dev, SYS_RES_IOPORT, 0, port_addr, 1))
15540160Simp			continue;
15640160Simp
15759082Snyan		/* XXX what is the real portsize? */
15859082Snyan		iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
15959082Snyan					   RF_ACTIVE);
16059082Snyan		if (iores == NULL)
16159082Snyan			continue;
16259082Snyan
16359082Snyan		if (adv_find_signature(rman_get_bustag(iores),
16459171Snyan				       rman_get_bushandle(iores)) == 0) {
16559171Snyan			bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
16659171Snyan			continue;
16759171Snyan		}
16818781Sgibbs
16959171Snyan		/*
17059171Snyan		 * Got one.  Now allocate our softc
17159171Snyan		 * and see if we can initialize the card.
17259171Snyan		 */
17359171Snyan		adv = adv_alloc(dev, rman_get_bustag(iores),
17459171Snyan				rman_get_bushandle(iores));
17559171Snyan		if (adv == NULL) {
17659171Snyan			bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
17776309Snyan			break;
17859171Snyan		}
17959171Snyan
18059171Snyan		/*
18159171Snyan		 * Stop the chip.
18259171Snyan		 */
18359171Snyan		ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_HALT);
18459171Snyan		ADV_OUTW(adv, ADV_CHIP_STATUS, 0);
18559171Snyan		/*
18659171Snyan		 * Determine the chip version.
18759171Snyan		 */
18859171Snyan		adv->chip_version = ADV_INB(adv, ADV_NONEISA_CHIP_REVISION);
18959171Snyan		if ((adv->chip_version >= ADV_CHIP_MIN_VER_VL)
19059171Snyan		    && (adv->chip_version <= ADV_CHIP_MAX_VER_VL)) {
19159171Snyan			adv->type = ADV_VL;
19259171Snyan			maxsegsz = ADV_VL_MAX_DMA_COUNT;
19359171Snyan			maxsize = BUS_SPACE_MAXSIZE_32BIT;
19459171Snyan			lowaddr = ADV_VL_MAX_DMA_ADDR;
19559171Snyan			bus_delete_resource(dev, SYS_RES_DRQ, 0);
19659171Snyan		} else if ((adv->chip_version >= ADV_CHIP_MIN_VER_ISA)
19759171Snyan			   && (adv->chip_version <= ADV_CHIP_MAX_VER_ISA)) {
19859171Snyan			if (adv->chip_version >= ADV_CHIP_MIN_VER_ISA_PNP) {
19959171Snyan				adv->type = ADV_ISAPNP;
20059171Snyan				ADV_OUTB(adv, ADV_REG_IFC,
20159171Snyan					 ADV_IFC_INIT_DEFAULT);
20239217Sgibbs			} else {
20359171Snyan				adv->type = ADV_ISA;
20439217Sgibbs			}
20559171Snyan			maxsegsz = ADV_ISA_MAX_DMA_COUNT;
20659171Snyan			maxsize = BUS_SPACE_MAXSIZE_24BIT;
20759171Snyan			lowaddr = ADV_ISA_MAX_DMA_ADDR;
20859171Snyan			adv->isa_dma_speed = ADV_DEF_ISA_DMA_SPEED;
20959171Snyan			adv->isa_dma_channel = adv_get_isa_dma_channel(adv);
21059171Snyan			bus_set_resource(dev, SYS_RES_DRQ, 0,
21159171Snyan					 adv->isa_dma_channel, 1);
21259171Snyan		} else {
21359171Snyan			panic("advisaprobe: Unknown card revision\n");
21459171Snyan		}
21539217Sgibbs
21659171Snyan		/*
21759171Snyan		 * Allocate a parent dmatag for all tags created
21859171Snyan		 * by the MI portions of the advansys driver
21959171Snyan		 */
22059171Snyan		/* XXX Should be a child of the ISA bus dma tag */
221112782Smdodd		error = bus_dma_tag_create(
222112782Smdodd				/* parent	*/ NULL,
223112782Smdodd				/* alignemnt	*/ 1,
224112782Smdodd				/* boundary	*/ 0,
225112782Smdodd				/* lowaddr	*/ lowaddr,
226112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
227112782Smdodd				/* filter	*/ NULL,
228112782Smdodd				/* filterarg	*/ NULL,
229112782Smdodd				/* maxsize	*/ maxsize,
230112782Smdodd				/* nsegments	*/ ~0,
231112782Smdodd				/* maxsegsz	*/ maxsegsz,
232112782Smdodd				/* flags	*/ 0,
233112782Smdodd				&adv->parent_dmat);
23459171Snyan
23559171Snyan		if (error != 0) {
23659171Snyan			printf("%s: Could not allocate DMA tag - error %d\n",
23759171Snyan			       adv_name(adv), error);
23859171Snyan			adv_free(adv);
23959171Snyan			bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
24076309Snyan			break;
24159171Snyan		}
24259171Snyan
24372147Sjhb		adv->init_level += 2;
24459171Snyan
24559171Snyan		if (overrun_buf == NULL) {
24659171Snyan			/* Need to allocate our overrun buffer */
247112782Smdodd			if (bus_dma_tag_create(
248112782Smdodd				/* parent	*/ adv->parent_dmat,
249112782Smdodd				/* alignment	*/ 8,
250112782Smdodd				/* boundary	*/ 0,
251112782Smdodd				/* lowaddr	*/ ADV_ISA_MAX_DMA_ADDR,
252112782Smdodd				/* highaddr	*/ BUS_SPACE_MAXADDR,
253112782Smdodd				/* filter	*/ NULL,
254112782Smdodd				/* filterarg	*/ NULL,
255112782Smdodd				/* maxsize	*/ ADV_OVERRUN_BSIZE,
256112782Smdodd				/* nsegments	*/ 1,
257112782Smdodd				/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
258112782Smdodd				/* flags	*/ 0,
259112782Smdodd				&overrun_dmat) != 0) {
26059171Snyan				adv_free(adv);
26159171Snyan				bus_release_resource(dev, SYS_RES_IOPORT, 0,
26259171Snyan						     iores);
26376309Snyan				break;
26439217Sgibbs			}
26559171Snyan			if (bus_dmamem_alloc(overrun_dmat,
26659171Snyan					     (void **)&overrun_buf,
26759171Snyan					     BUS_DMA_NOWAIT,
26859171Snyan					     &overrun_dmamap) != 0) {
26959171Snyan				bus_dma_tag_destroy(overrun_dmat);
27018781Sgibbs				adv_free(adv);
27159171Snyan				bus_release_resource(dev, SYS_RES_IOPORT, 0,
27259171Snyan						     iores);
27376309Snyan				break;
27418781Sgibbs			}
27559171Snyan			/* And permanently map it in */
27659171Snyan			bus_dmamap_load(overrun_dmat, overrun_dmamap,
27759171Snyan					overrun_buf, ADV_OVERRUN_BSIZE,
27859171Snyan					adv_map, &overrun_physbase,
27959171Snyan					/*flags*/0);
28059171Snyan		}
28139217Sgibbs
28259171Snyan		adv->overrun_physbase = overrun_physbase;
28318781Sgibbs
28459171Snyan		if (adv_init(adv) != 0) {
28572147Sjhb			bus_dmamap_unload(overrun_dmat, overrun_dmamap);
28672147Sjhb			bus_dmamem_free(overrun_dmat, overrun_buf,
28772147Sjhb			    overrun_dmamap);
28872147Sjhb			bus_dma_tag_destroy(overrun_dmat);
28959171Snyan			adv_free(adv);
29059171Snyan			bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
29176309Snyan			break;
29259171Snyan		}
29359171Snyan
29459171Snyan		switch (adv->type) {
29559171Snyan		case ADV_ISAPNP:
29659171Snyan			if (adv->chip_version == ADV_CHIP_VER_ASYN_BUG) {
29759171Snyan				adv->bug_fix_control
29859171Snyan				    |= ADV_BUG_FIX_ASYN_USE_SYN;
29959171Snyan				adv->fix_asyn_xfer = ~0;
30018781Sgibbs			}
30159171Snyan			/* Fall Through */
30259171Snyan		case ADV_ISA:
30359171Snyan			adv->max_dma_count = ADV_ISA_MAX_DMA_COUNT;
30459171Snyan			adv->max_dma_addr = ADV_ISA_MAX_DMA_ADDR;
30559171Snyan			adv_set_isa_dma_settings(adv);
30659171Snyan			break;
30759171Snyan
30859171Snyan		case ADV_VL:
30959171Snyan			adv->max_dma_count = ADV_VL_MAX_DMA_COUNT;
31059171Snyan			adv->max_dma_addr = ADV_VL_MAX_DMA_ADDR;
31159171Snyan			break;
31259171Snyan		default:
31359171Snyan			panic("advisaprobe: Invalid card type\n");
31459171Snyan		}
31518781Sgibbs
31659171Snyan		/* Determine our IRQ */
31759171Snyan		if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL))
31859171Snyan			bus_set_resource(dev, SYS_RES_IRQ, 0,
31959171Snyan					 adv_get_chip_irq(adv), 1);
32059171Snyan		else
32159171Snyan			adv_set_chip_irq(adv, irq);
32240160Simp
32359171Snyan		irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
32459171Snyan					    RF_ACTIVE);
32559171Snyan		if (irqres == NULL ||
32676309Snyan		    bus_setup_intr(dev, irqres, INTR_TYPE_CAM|INTR_ENTROPY,
32776309Snyan				   adv_intr, adv, &ih)) {
32872147Sjhb			bus_dmamap_unload(overrun_dmat, overrun_dmamap);
32972147Sjhb			bus_dmamem_free(overrun_dmat, overrun_buf,
33072147Sjhb			    overrun_dmamap);
33172147Sjhb			bus_dma_tag_destroy(overrun_dmat);
33259171Snyan			adv_free(adv);
33359171Snyan			bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
33476309Snyan			break;
33559171Snyan		}
33659082Snyan
33759171Snyan		/* Mark as probed */
33859171Snyan		adv_isa_ioports[port_index] = 0;
33959171Snyan		return 0;
34018781Sgibbs	}
34118781Sgibbs
34276309Snyan	if (user_iobase)
34376309Snyan		bus_set_resource(dev, SYS_RES_IOPORT, 0, iobase, iocount);
34476309Snyan	else
34576309Snyan		bus_delete_resource(dev, SYS_RES_IOPORT, 0);
34676309Snyan
34759082Snyan	return ENXIO;
34818781Sgibbs}
34918781Sgibbs
35018781Sgibbsstatic int
35159082Snyanadv_isa_attach(device_t dev)
35218781Sgibbs{
35359082Snyan	struct adv_softc *adv = device_get_softc(dev);
35418781Sgibbs
35518781Sgibbs	return (adv_attach(adv));
35618781Sgibbs}
35718781Sgibbs
35839217Sgibbsstatic int
35939217Sgibbsadv_get_isa_dma_channel(struct adv_softc *adv)
36039217Sgibbs{
36139217Sgibbs	int channel;
36239217Sgibbs
36339217Sgibbs	channel = ADV_INW(adv, ADV_CONFIG_LSW) & ADV_CFG_LSW_ISA_DMA_CHANNEL;
36439217Sgibbs	if (channel == 0x03)
36539217Sgibbs		return (0);
36639217Sgibbs	else if (channel == 0x00)
36739217Sgibbs		return (7);
36839217Sgibbs	return (channel + 4);
36939217Sgibbs}
37039217Sgibbs
37139217Sgibbsstatic int
37239217Sgibbsadv_set_isa_dma_settings(struct adv_softc *adv)
37339217Sgibbs{
37439217Sgibbs	u_int16_t cfg_lsw;
37539217Sgibbs	u_int8_t  value;
37639217Sgibbs
37739217Sgibbs	if ((adv->isa_dma_channel >= 5) && (adv->isa_dma_channel <= 7)) {
37839217Sgibbs	        if (adv->isa_dma_channel == 7)
37939217Sgibbs			value = 0x00;
38039217Sgibbs		else
38139217Sgibbs			value = adv->isa_dma_channel - 4;
38239217Sgibbs		cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW)
38339217Sgibbs			& ~ADV_CFG_LSW_ISA_DMA_CHANNEL;
38439217Sgibbs		cfg_lsw |= value;
38539217Sgibbs		ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
38639217Sgibbs
38739217Sgibbs		adv->isa_dma_speed &= 0x07;
38839217Sgibbs		adv_set_bank(adv, 1);
38939217Sgibbs		ADV_OUTB(adv, ADV_DMA_SPEED, adv->isa_dma_speed);
39039217Sgibbs		adv_set_bank(adv, 0);
39139217Sgibbs		isa_dmacascade(adv->isa_dma_channel);
39239217Sgibbs	}
39339217Sgibbs	return (0);
39439217Sgibbs}
39539217Sgibbs
39618781Sgibbsstatic void
39718781Sgibbsadv_set_isapnp_wait_for_key(void)
39818781Sgibbs{
39918781Sgibbs	static	int isapnp_wait_set = 0;
40018781Sgibbs	if (isapnp_wait_set == 0) {
40118781Sgibbs		outb(ADV_ISA_PNP_PORT_ADDR, 0x02);
40218781Sgibbs		outb(ADV_ISA_PNP_PORT_WRITE, 0x02);
40318781Sgibbs		isapnp_wait_set++;
40418781Sgibbs	}
40518781Sgibbs}
40618781Sgibbs
40759082Snyanstatic device_method_t adv_isa_methods[] = {
40859082Snyan	/* Device interface */
40959082Snyan	DEVMETHOD(device_probe,		adv_isa_probe),
41059082Snyan	DEVMETHOD(device_attach,	adv_isa_attach),
41159082Snyan	{ 0, 0 }
41259082Snyan};
41359082Snyan
41459082Snyanstatic driver_t adv_isa_driver = {
41559082Snyan	"adv", adv_isa_methods, sizeof(struct adv_softc)
41659082Snyan};
41759082Snyan
41859082Snyanstatic devclass_t adv_isa_devclass;
41959082SnyanDRIVER_MODULE(adv, isa, adv_isa_driver, adv_isa_devclass, 0, 0);
420