Subject: Re: AHA 1740 driver: ahb0: host_stat 18
To: Sean Berry (most of the time) <spberry@iastate.edu>
From: Charles M. Hannum <mycroft@mit.edu>
List: port-i386
Date: 11/09/1996 10:15:44
Sean Berry (most of the time) <spberry@iastate.edu> writes:

> 
> I was able to put a BSD install onto one of the disks (set to scsi id 0),
> and the machine successfully loaded bootblocks, and kernel.  Unfortunately,
> after the probe, where it should probe for disks on the controller, I got
> the error message: ahb0: host_stat 18.

I don't have any 174x documentation, but another piece of code claims
that this means `invalid control block parameter'.

I presume you're running 1.2?  If so, could you try the attached
version of the 174x driver (pulled from -current), and let me know
whether the behaviour changes?


# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	ahb.c
#	ahbreg.h
#	files.eisa
#
echo x - ahb.c
sed 's/^X//' >ahb.c << 'END-of-ahb.c'
X/*	$NetBSD$	*/
X
X#undef	AHBDEBUG
X#ifdef DDB
X#define	integrate
X#else
X#define	integrate	static inline
X#endif
X
X/*
X * Copyright (c) 1994, 1996 Charles M. Hannum.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by Charles M. Hannum.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Originally written by Julian Elischer (julian@tfs.com)
X * for TRW Financial Systems for use under the MACH(2.5) operating system.
X *
X * TRW Financial Systems, in accordance with their agreement with Carnegie
X * Mellon University, makes this software available to CMU to distribute
X * or use in any manner that they see fit as long as this message is kept with
X * the software. For this reason TFS also grants any other persons or
X * organisations permission to use or modify this software.
X *
X * TFS supplies this software to be publicly redistributed
X * on the understanding that TFS is not responsible for the correct
X * functioning of this software in any circumstances.
X */
X
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/errno.h>
X#include <sys/ioctl.h>
X#include <sys/device.h>
X#include <sys/malloc.h>
X#include <sys/buf.h>
X#include <sys/proc.h>
X#include <sys/user.h>
X
X#include <machine/bus.h>
X#include <machine/intr.h>
X
X#include <scsi/scsi_all.h>
X#include <scsi/scsiconf.h>
X
X#include <dev/eisa/eisareg.h>
X#include <dev/eisa/eisavar.h>
X#include <dev/eisa/eisadevs.h>
X#include <dev/eisa/ahbreg.h>
X
X#ifndef DDB
X#define Debugger() panic("should call debugger here (aha1742.c)")
X#endif /* ! DDB */
X
X#define AHB_ECB_MAX	32	/* store up to 32 ECBs at one time */
X#define	ECB_HASH_SIZE	32	/* hash table size for phystokv */
X#define	ECB_HASH_SHIFT	9
X#define ECB_HASH(x)	((((long)(x))>>ECB_HASH_SHIFT) & (ECB_HASH_SIZE - 1))
X
X#define	KVTOPHYS(x)	vtophys(x)
X
Xstruct ahb_softc {
X	struct device sc_dev;
X	bus_chipset_tag_t sc_bc;
X
X	bus_io_handle_t sc_ioh;
X	int sc_irq;
X	void *sc_ih;
X
X	struct ahb_ecb *sc_ecbhash[ECB_HASH_SIZE];
X	TAILQ_HEAD(, ahb_ecb) sc_free_ecb;
X	struct ahb_ecb *sc_immed_ecb;	/* an outstanding immediete command */
X	int sc_numecbs;
X	int sc_scsi_dev;		/* our scsi id */
X	struct scsi_link sc_link;
X};
X
Xvoid ahb_send_mbox __P((struct ahb_softc *, int, struct ahb_ecb *));
Xvoid ahb_send_immed __P((struct ahb_softc *, u_long, struct ahb_ecb *));
Xint ahbintr __P((void *));
Xvoid ahb_free_ecb __P((struct ahb_softc *, struct ahb_ecb *));
Xstruct ahb_ecb *ahb_get_ecb __P((struct ahb_softc *, int));
Xstruct ahb_ecb *ahb_ecb_phys_kv __P((struct ahb_softc *, physaddr));
Xvoid ahb_done __P((struct ahb_softc *, struct ahb_ecb *));
Xint ahb_find __P((bus_chipset_tag_t, bus_io_handle_t, struct ahb_softc *));
Xvoid ahb_init __P((struct ahb_softc *));
Xvoid ahbminphys __P((struct buf *));
Xint ahb_scsi_cmd __P((struct scsi_xfer *));
Xint ahb_poll __P((struct ahb_softc *, struct scsi_xfer *, int));
Xvoid ahb_timeout __P((void *));
X
Xintegrate void ahb_reset_ecb __P((struct ahb_softc *, struct ahb_ecb *));
Xintegrate void ahb_init_ecb __P((struct ahb_softc *, struct ahb_ecb *));
X
Xstruct scsi_adapter ahb_switch = {
X	ahb_scsi_cmd,
X	ahbminphys,
X	0,
X	0,
X};
X
X/* the below structure is so we have a default dev struct for our link struct */
Xstruct scsi_device ahb_dev = {
X	NULL,			/* Use default error handler */
X	NULL,			/* have a queue, served by this */
X	NULL,			/* have no async handler */
X	NULL,			/* Use default 'done' routine */
X};
X
Xint	ahbmatch __P((struct device *, void *, void *));
Xvoid	ahbattach __P((struct device *, struct device *, void *));
X
Xstruct cfattach ahb_ca = {
X	sizeof(struct ahb_softc), ahbmatch, ahbattach
X};
X
Xstruct cfdriver ahb_cd = {
X	NULL, "ahb", DV_DULL
X};
X
X#define	AHB_ABORT_TIMEOUT	2000	/* time to wait for abort (mSec) */
X
X/*
X * Check the slots looking for a board we recognise
X * If we find one, note it's address (slot) and call
X * the actual probe routine to check it out.
X */
Xint
Xahbmatch(parent, match, aux)
X	struct device *parent;
X	void *match, *aux;
X{
X	struct eisa_attach_args *ea = aux;
X	bus_chipset_tag_t bc = ea->ea_bc;
X	bus_io_handle_t ioh;
X	int rv;
X
X	/* must match one of our known ID strings */
X	if (strcmp(ea->ea_idstring, "ADP0000") &&
X	    strcmp(ea->ea_idstring, "ADP0001") &&
X	    strcmp(ea->ea_idstring, "ADP0002") &&
X	    strcmp(ea->ea_idstring, "ADP0400"))
X		return (0);
X
X	if (bus_io_map(bc, EISA_SLOT_ADDR(ea->ea_slot), EISA_SLOT_SIZE, &ioh))
X		return (0);
X
X	rv = !ahb_find(bc, ioh, NULL);
X
X	bus_io_unmap(bc, ioh, EISA_SLOT_SIZE);
X
X	return (rv);
X}
X
Xahbprint()
X{
X
X}
X
X/*
X * Attach all the sub-devices we can find
X */
Xvoid
Xahbattach(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	struct eisa_attach_args *ea = aux;
X	struct ahb_softc *sc = (void *)self;
X	bus_chipset_tag_t bc = ea->ea_bc;
X	bus_io_handle_t ioh;
X	eisa_chipset_tag_t ec = ea->ea_ec;
X	eisa_intr_handle_t ih;
X	const char *model, *intrstr;
X
X	if (!strcmp(ea->ea_idstring, "ADP0000"))
X		model = EISA_PRODUCT_ADP0000;
X	else if (!strcmp(ea->ea_idstring, "ADP0001"))
X		model = EISA_PRODUCT_ADP0001;
X	else if (!strcmp(ea->ea_idstring, "ADP0002"))
X		model = EISA_PRODUCT_ADP0002;
X	else if (!strcmp(ea->ea_idstring, "ADP0400"))
X		model = EISA_PRODUCT_ADP0400;
X	else
X		model = "unknown model!";
X	printf(": %s\n", model);
X
X	if (bus_io_map(bc, EISA_SLOT_ADDR(ea->ea_slot), EISA_SLOT_SIZE, &ioh))
X		panic("ahbattach: could not map I/O addresses");
X
X	sc->sc_bc = bc;
X	sc->sc_ioh = ioh;
X	if (ahb_find(bc, ioh, sc))
X		panic("ahbattach: ahb_find failed!");
X
X	ahb_init(sc);
X	TAILQ_INIT(&sc->sc_free_ecb);
X
X	/*
X	 * fill in the prototype scsi_link.
X	 */
X	sc->sc_link.adapter_softc = sc;
X	sc->sc_link.adapter_target = sc->sc_scsi_dev;
X	sc->sc_link.adapter = &ahb_switch;
X	sc->sc_link.device = &ahb_dev;
X	sc->sc_link.openings = 4;
X
X	if (eisa_intr_map(ec, sc->sc_irq, &ih)) {
X		printf("%s: couldn't map interrupt (%d)\n",
X		    sc->sc_dev.dv_xname, sc->sc_irq);
X		return;
X	}
X	intrstr = eisa_intr_string(ec, ih);
X	sc->sc_ih = eisa_intr_establish(ec, ih, IST_LEVEL, IPL_BIO,
X	    ahbintr, sc);
X	if (sc->sc_ih == NULL) {
X		printf("%s: couldn't establish interrupt",
X		    sc->sc_dev.dv_xname);
X		if (intrstr != NULL)
X			printf(" at %s", intrstr);
X		printf("\n");
X		return;
X	}
X	if (intrstr != NULL)
X		printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname,
X		    intrstr);
X
X	/*
X	 * ask the adapter what subunits are present
X	 */
X	config_found(self, &sc->sc_link, ahbprint);
X}
X
X/*
X * Function to send a command out through a mailbox
X */
Xvoid
Xahb_send_mbox(sc, opcode, ecb)
X	struct ahb_softc *sc;
X	int opcode;
X	struct ahb_ecb *ecb;
X{
X	bus_chipset_tag_t bc = sc->sc_bc;
X	bus_io_handle_t ioh = sc->sc_ioh;
X	int wait = 300;	/* 1ms should be enough */
X
X	while (--wait) {
X		if ((bus_io_read_1(bc, ioh, G2STAT) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
X		    == (G2STAT_MBOX_EMPTY))
X			break;
X		delay(10);
X	}
X	if (!wait) {
X		printf("%s: board not responding\n", sc->sc_dev.dv_xname);
X		Debugger();
X	}
X
X	bus_io_write_4(bc, ioh, MBOXOUT0, KVTOPHYS(ecb)); /* don't know this will work */
X	bus_io_write_1(bc, ioh, ATTN, opcode | ecb->xs->sc_link->target);
X
X	if ((ecb->xs->flags & SCSI_POLL) == 0)
X		timeout(ahb_timeout, ecb, (ecb->timeout * hz) / 1000);
X}
X
X/*
X * Function to  send an immediate type command to the adapter
X */
Xvoid
Xahb_send_immed(sc, cmd, ecb)
X	struct ahb_softc *sc;
X	u_long cmd;
X	struct ahb_ecb *ecb;
X{
X	bus_chipset_tag_t bc = sc->sc_bc;
X	bus_io_handle_t ioh = sc->sc_ioh;
X	int wait = 100;	/* 1 ms enough? */
X
X	while (--wait) {
X		if ((bus_io_read_1(bc, ioh, G2STAT) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
X		    == (G2STAT_MBOX_EMPTY))
X			break;
X		delay(10);
X	}
X	if (!wait) {
X		printf("%s: board not responding\n", sc->sc_dev.dv_xname);
X		Debugger();
X	}
X
X	bus_io_write_4(bc, ioh, MBOXOUT0, cmd);	/* don't know this will work */
X	bus_io_write_1(bc, ioh, G2CNTRL, G2CNTRL_SET_HOST_READY);
X	bus_io_write_1(bc, ioh, ATTN, OP_IMMED | ecb->xs->sc_link->target);
X
X	if ((ecb->xs->flags & SCSI_POLL) == 0)
X		timeout(ahb_timeout, ecb, (ecb->timeout * hz) / 1000);
X}
X
X/*
X * Catch an interrupt from the adaptor
X */
Xint
Xahbintr(arg)
X	void *arg;
X{
X	struct ahb_softc *sc = arg;
X	bus_chipset_tag_t bc = sc->sc_bc;
X	bus_io_handle_t ioh = sc->sc_ioh;
X	struct ahb_ecb *ecb;
X	u_char ahbstat;
X	u_long mboxval;
X
X#ifdef	AHBDEBUG
X	printf("%s: ahbintr ", sc->sc_dev.dv_xname);
X#endif /* AHBDEBUG */
X
X	if ((bus_io_read_1(bc, ioh, G2STAT) & G2STAT_INT_PEND) == 0)
X		return 0;
X
X	for (;;) {
X		/*
X		 * First get all the information and then
X		 * acknowlege the interrupt
X		 */
X		ahbstat = bus_io_read_1(bc, ioh, G2INTST);
X		mboxval = bus_io_read_4(bc, ioh, MBOXIN0);
X		bus_io_write_1(bc, ioh, G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
X
X#ifdef	AHBDEBUG
X		printf("status = 0x%x ", ahbstat);
X#endif /* AHBDEBUG */
X
X		/*
X		 * Process the completed operation
X		 */
X		switch (ahbstat & G2INTST_INT_STAT) {
X		case AHB_ECB_OK:
X		case AHB_ECB_RECOVERED:
X		case AHB_ECB_ERR:
X			ecb = ahb_ecb_phys_kv(sc, mboxval);
X			if (!ecb) {
X				printf("%s: BAD ECB RETURNED!\n",
X				    sc->sc_dev.dv_xname);
X				goto next;	/* whatever it was, it'll timeout */
X			}
X			break;
X
X		case AHB_IMMED_ERR:
X			ecb = sc->sc_immed_ecb;
X			sc->sc_immed_ecb = 0;
X			ecb->flags |= ECB_IMMED_FAIL;
X			break;
X
X		case AHB_IMMED_OK:
X			ecb = sc->sc_immed_ecb;
X			sc->sc_immed_ecb = 0;
X			break;
X
X		default:
X			printf("%s: unexpected interrupt %x\n",
X			    sc->sc_dev.dv_xname, ahbstat);
X			goto next;
X		}
X
X		untimeout(ahb_timeout, ecb);
X		ahb_done(sc, ecb);
X
X	next:
X		if ((bus_io_read_1(bc, ioh, G2STAT) & G2STAT_INT_PEND) == 0)
X			return 1;
X	}
X}
X
Xintegrate void
Xahb_reset_ecb(sc, ecb)
X	struct ahb_softc *sc;
X	struct ahb_ecb *ecb;
X{
X
X	ecb->flags = 0;
X}
X
X/*
X * A ecb (and hence a mbx-out is put onto the
X * free list.
X */
Xvoid
Xahb_free_ecb(sc, ecb)
X	struct ahb_softc *sc;
X	struct ahb_ecb *ecb;
X{
X	int s;
X
X	s = splbio();
X
X	ahb_reset_ecb(sc, ecb);
X	TAILQ_INSERT_HEAD(&sc->sc_free_ecb, ecb, chain);
X
X	/*
X	 * If there were none, wake anybody waiting for one to come free,
X	 * starting with queued entries.
X	 */
X	if (ecb->chain.tqe_next == 0)
X		wakeup(&sc->sc_free_ecb);
X
X	splx(s);
X}
X
Xintegrate void
Xahb_init_ecb(sc, ecb)
X	struct ahb_softc *sc;
X	struct ahb_ecb *ecb;
X{
X	int hashnum;
X
X	bzero(ecb, sizeof(struct ahb_ecb));
X	/*
X	 * put in the phystokv hash table
X	 * Never gets taken out.
X	 */
X	ecb->hashkey = KVTOPHYS(ecb);
X	hashnum = ECB_HASH(ecb->hashkey);
X	ecb->nexthash = sc->sc_ecbhash[hashnum];
X	sc->sc_ecbhash[hashnum] = ecb;
X	ahb_reset_ecb(sc, ecb);
X}
X
X/*
X * Get a free ecb
X *
X * If there are none, see if we can allocate a new one. If so, put it in the
X * hash table too otherwise either return an error or sleep.
X */
Xstruct ahb_ecb *
Xahb_get_ecb(sc, flags)
X	struct ahb_softc *sc;
X	int flags;
X{
X	struct ahb_ecb *ecb;
X	int s;
X
X	s = splbio();
X
X	/*
X	 * If we can and have to, sleep waiting for one to come free
X	 * but only if we can't allocate a new one.
X	 */
X	for (;;) {
X		ecb = sc->sc_free_ecb.tqh_first;
X		if (ecb) {
X			TAILQ_REMOVE(&sc->sc_free_ecb, ecb, chain);
X			break;
X		}
X		if (sc->sc_numecbs < AHB_ECB_MAX) {
X			ecb = (struct ahb_ecb *) malloc(sizeof(struct ahb_ecb),
X			    M_TEMP, M_NOWAIT);
X			if (!ecb) {
X				printf("%s: can't malloc ecb\n",
X				    sc->sc_dev.dv_xname);
X				goto out;
X			}
X			ahb_init_ecb(sc, ecb);
X			sc->sc_numecbs++;
X			break;
X		}
X		if ((flags & SCSI_NOSLEEP) != 0)
X			goto out;
X		tsleep(&sc->sc_free_ecb, PRIBIO, "ahbecb", 0);
X	}
X
X	ecb->flags |= ECB_ALLOC;
X
Xout:
X	splx(s);
X	return ecb;
X}
X
X/*
X * given a physical address, find the ecb that it corresponds to.
X */
Xstruct ahb_ecb *
Xahb_ecb_phys_kv(sc, ecb_phys)
X	struct ahb_softc *sc;
X	physaddr ecb_phys;
X{
X	int hashnum = ECB_HASH(ecb_phys);
X	struct ahb_ecb *ecb = sc->sc_ecbhash[hashnum];
X
X	while (ecb) {
X		if (ecb->hashkey == ecb_phys)
X			break;
X		ecb = ecb->nexthash;
X	}
X	return ecb;
X}
X
X/*
X * We have a ecb which has been processed by the adaptor, now we look to see
X * how the operation went.
X */
Xvoid
Xahb_done(sc, ecb)
X	struct ahb_softc *sc;
X	struct ahb_ecb *ecb;
X{
X	struct scsi_sense_data *s1, *s2;
X	struct scsi_xfer *xs = ecb->xs;
X
X	SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahb_done\n"));
X	/*
X	 * Otherwise, put the results of the operation
X	 * into the xfer and call whoever started it
X	 */
X	if ((ecb->flags & ECB_ALLOC) == 0) {
X		printf("%s: exiting ecb not allocated!\n", sc->sc_dev.dv_xname);
X		Debugger();
X	}
X	if (ecb->flags & ECB_IMMED) {
X		if (ecb->flags & ECB_IMMED_FAIL)
X			xs->error = XS_DRIVER_STUFFUP;
X		goto done;
X	}
X	if (xs->error == XS_NOERROR) {
X		if (ecb->ecb_status.host_stat != HS_OK) {
X			switch (ecb->ecb_status.host_stat) {
X			case HS_TIMED_OUT:	/* No response */
X				xs->error = XS_SELTIMEOUT;
X				break;
X			default:	/* Other scsi protocol messes */
X				printf("%s: host_stat %x\n",
X				    sc->sc_dev.dv_xname, ecb->ecb_status.host_stat);
X				xs->error = XS_DRIVER_STUFFUP;
X			}
X		} else if (ecb->ecb_status.target_stat != SCSI_OK) {
X			switch (ecb->ecb_status.target_stat) {
X			case SCSI_CHECK:
X				s1 = &ecb->ecb_sense;
X				s2 = &xs->sense;
X				*s2 = *s1;
X				xs->error = XS_SENSE;
X				break;
X			case SCSI_BUSY:
X				xs->error = XS_BUSY;
X				break;
X			default:
X				printf("%s: target_stat %x\n",
X				    sc->sc_dev.dv_xname, ecb->ecb_status.target_stat);
X				xs->error = XS_DRIVER_STUFFUP;
X			}
X		} else
X			xs->resid = 0;
X	}
Xdone:
X	ahb_free_ecb(sc, ecb);
X	xs->flags |= ITSDONE;
X	scsi_done(xs);
X}
X
X/*
X * Start the board, ready for normal operation
X */
Xint
Xahb_find(bc, ioh, sc)
X	bus_chipset_tag_t bc;
X	bus_io_handle_t ioh;
X	struct ahb_softc *sc;
X{
X	u_char intdef;
X	int i, irq, busid;
X	int wait = 1000;	/* 1 sec enough? */
X
X	bus_io_write_1(bc, ioh, PORTADDR, PORTADDR_ENHANCED);
X
X#define	NO_NO 1
X#ifdef NO_NO
X	/*
X	 * reset board, If it doesn't respond, assume
X	 * that it's not there.. good for the probe
X	 */
X	bus_io_write_1(bc, ioh, G2CNTRL, G2CNTRL_HARD_RESET);
X	delay(1000);
X	bus_io_write_1(bc, ioh, G2CNTRL, 0);
X	delay(10000);
X	while (--wait) {
X		if ((bus_io_read_1(bc, ioh, G2STAT) & G2STAT_BUSY) == 0)
X			break;
X		delay(1000);
X	}
X	if (!wait) {
X#ifdef	AHBDEBUG
X		printf("ahb_find: No answer from aha1742 board\n");
X#endif /* AHBDEBUG */
X		return ENXIO;
X	}
X	i = bus_io_read_1(bc, ioh, MBOXIN0);
X	if (i) {
X		printf("self test failed, val = 0x%x\n", i);
X		return EIO;
X	}
X
X	/* Set it again, just to be sure. */
X	bus_io_write_1(bc, ioh, PORTADDR, PORTADDR_ENHANCED);
X#endif
X
X	while (bus_io_read_1(bc, ioh, G2STAT) & G2STAT_INT_PEND) {
X		printf(".");
X		bus_io_write_1(bc, ioh, G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
X		delay(10000);
X	}
X
X	intdef = bus_io_read_1(bc, ioh, INTDEF);
X	switch (intdef & 0x07) {
X	case INT9:
X		irq = 9;
X		break;
X	case INT10:
X		irq = 10;
X		break;
X	case INT11:
X		irq = 11;
X		break;
X	case INT12:
X		irq = 12;
X		break;
X	case INT14:
X		irq = 14;
X		break;
X	case INT15:
X		irq = 15;
X		break;
X	default:
X		printf("illegal int setting %x\n", intdef);
X		return EIO;
X	}
X
X	bus_io_write_1(bc, ioh, INTDEF, (intdef | INTEN));	/* make sure we can interrupt */
X
X	/* who are we on the scsi bus? */
X	busid = (bus_io_read_1(bc, ioh, SCSIDEF) & HSCSIID);
X
X	/* if we want to fill in softc, do so now */
X	if (sc != NULL) {
X		sc->sc_irq = irq;
X		sc->sc_scsi_dev = busid;
X	}
X
X	/*
X	 * Note that we are going and return (to probe)
X	 */
X	return 0;
X}
X
Xvoid
Xahb_init(sc)
X	struct ahb_softc *sc;
X{
X
X}
X
Xvoid
Xahbminphys(bp)
X	struct buf *bp;
X{
X
X	if (bp->b_bcount > ((AHB_NSEG - 1) << PGSHIFT))
X		bp->b_bcount = ((AHB_NSEG - 1) << PGSHIFT);
X	minphys(bp);
X}
X
X/*
X * start a scsi operation given the command and the data address.  Also needs
X * the unit, target and lu.
X */
Xint
Xahb_scsi_cmd(xs)
X	struct scsi_xfer *xs;
X{
X	struct scsi_link *sc_link = xs->sc_link;
X	struct ahb_softc *sc = sc_link->adapter_softc;
X	struct ahb_ecb *ecb;
X	struct ahb_dma_seg *sg;
X	int seg;		/* scatter gather seg being worked on */
X	u_long thiskv, thisphys, nextphys;
X	int bytes_this_seg, bytes_this_page, datalen, flags;
X	int s;
X
X	SC_DEBUG(sc_link, SDEV_DB2, ("ahb_scsi_cmd\n"));
X	/*
X	 * get a ecb (mbox-out) to use. If the transfer
X	 * is from a buf (possibly from interrupt time)
X	 * then we can't allow it to sleep
X	 */
X	flags = xs->flags;
X	if ((ecb = ahb_get_ecb(sc, flags)) == NULL) {
X		xs->error = XS_DRIVER_STUFFUP;
X		return TRY_AGAIN_LATER;
X	}
X	ecb->xs = xs;
X	ecb->timeout = xs->timeout;
X
X	/*
X	 * If it's a reset, we need to do an 'immediate'
X	 * command, and store its ecb for later
X	 * if there is already an immediate waiting,
X	 * then WE must wait
X	 */
X	if (flags & SCSI_RESET) {
X		ecb->flags |= ECB_IMMED;
X		if (sc->sc_immed_ecb)
X			return TRY_AGAIN_LATER;
X		sc->sc_immed_ecb = ecb;
X
X		s = splbio();
X		ahb_send_immed(sc, AHB_TARG_RESET, ecb);
X		splx(s);
X
X		if ((flags & SCSI_POLL) == 0)
X			return SUCCESSFULLY_QUEUED;
X
X		/*
X		 * If we can't use interrupts, poll on completion
X		 */
X		if (ahb_poll(sc, xs, ecb->timeout))
X			ahb_timeout(ecb);
X		return COMPLETE;
X	}
X
X	/*
X	 * Put all the arguments for the xfer in the ecb
X	 */
X	ecb->opcode = ECB_SCSI_OP;
X	ecb->opt1 = ECB_SES /*| ECB_DSB*/ | ECB_ARS;
X	ecb->opt2 = sc_link->lun | ECB_NRB;
X	bcopy(xs->cmd, &ecb->scsi_cmd, ecb->scsi_cmd_length = xs->cmdlen);
X	ecb->sense_ptr = KVTOPHYS(&ecb->ecb_sense);
X	ecb->req_sense_length = sizeof(ecb->ecb_sense);
X	ecb->status = KVTOPHYS(&ecb->ecb_status);
X	ecb->ecb_status.host_stat = 0x00;
X	ecb->ecb_status.target_stat = 0x00;
X
X	if (xs->datalen) {
X		sg = ecb->ahb_dma;
X		seg = 0;
X#ifdef	TFS
X		if (flags & SCSI_DATA_UIO) {
X			struct iovec *iovp = ((struct uio *) xs->data)->uio_iov;
X			datalen = ((struct uio *) xs->data)->uio_iovcnt;
X			xs->datalen = 0;
X			while (datalen && seg < AHB_NSEG) {
X				sg->seg_addr = (physaddr)iovp->iov_base;
X				sg->seg_len = iovp->iov_len;
X				xs->datalen += iovp->iov_len;
X				SC_DEBUGN(sc_link, SDEV_DB4, ("(0x%x@0x%x)",
X				    iovp->iov_len, iovp->iov_base));
X				sg++;
X				iovp++;
X				seg++;
X				datalen--;
X			}
X		}
X		else
X#endif /*TFS */
X		{
X			/*
X			 * Set up the scatter gather block
X			 */
X			SC_DEBUG(sc_link, SDEV_DB4,
X			    ("%d @0x%x:- ", xs->datalen, xs->data));
X			datalen = xs->datalen;
X			thiskv = (long) xs->data;
X			thisphys = KVTOPHYS(thiskv);
X
X			while (datalen && seg < AHB_NSEG) {
X				bytes_this_seg = 0;
X
X				/* put in the base address */
X				sg->seg_addr = thisphys;
X
X				SC_DEBUGN(sc_link, SDEV_DB4, ("0x%x", thisphys));
X
X				/* do it at least once */
X				nextphys = thisphys;
X				while (datalen && thisphys == nextphys) {
X					/*
X					 * This page is contiguous (physically)
X					 * with the the last, just extend the
X					 * length
X					 */
X					/* how far to the end of the page */
X					nextphys = (thisphys & ~PGOFSET) + NBPG;
X					bytes_this_page = nextphys - thisphys;
X					/**** or the data ****/
X					bytes_this_page = min(bytes_this_page,
X							      datalen);
X					bytes_this_seg += bytes_this_page;
X					datalen -= bytes_this_page;
X
X					/* get more ready for the next page */
X					thiskv = (thiskv & ~PGOFSET) + NBPG;
X					if (datalen)
X						thisphys = KVTOPHYS(thiskv);
X				}
X				/*
X				 * next page isn't contiguous, finish the seg
X				 */
X				SC_DEBUGN(sc_link, SDEV_DB4,
X				    ("(0x%x)", bytes_this_seg));
X				sg->seg_len = bytes_this_seg;
X				sg++;
X				seg++;
X			}
X		}
X		/*end of iov/kv decision */
X		SC_DEBUGN(sc_link, SDEV_DB4, ("\n"));
X		if (datalen) {
X			/*
X			 * there's still data, must have run out of segs!
X			 */
X			printf("%s: ahb_scsi_cmd, more than %d dma segs\n",
X			    sc->sc_dev.dv_xname, AHB_NSEG);
X			goto bad;
X		}
X		ecb->data_addr = KVTOPHYS(ecb->ahb_dma);
X		ecb->data_length = seg * sizeof(struct ahb_dma_seg);
X		ecb->opt1 |= ECB_S_G;
X	} else {	/* No data xfer, use non S/G values */
X		ecb->data_addr = (physaddr)0;
X		ecb->data_length = 0;
X	}
X	ecb->link_addr = (physaddr)0;
X
X	s = splbio();
X	ahb_send_mbox(sc, OP_START_ECB, ecb);
X	splx(s);
X
X	/*
X	 * Usually return SUCCESSFULLY QUEUED
X	 */
X	if ((flags & SCSI_POLL) == 0)
X		return SUCCESSFULLY_QUEUED;
X
X	/*
X	 * If we can't use interrupts, poll on completion
X	 */
X	if (ahb_poll(sc, xs, ecb->timeout)) {
X		ahb_timeout(ecb);
X		if (ahb_poll(sc, xs, ecb->timeout))
X			ahb_timeout(ecb);
X	}
X	return COMPLETE;
X
Xbad:
X	xs->error = XS_DRIVER_STUFFUP;
X	ahb_free_ecb(sc, ecb);
X	return COMPLETE;
X}
X
X/*
X * Function to poll for command completion when in poll mode
X */
Xint
Xahb_poll(sc, xs, count)
X	struct ahb_softc *sc;
X	struct scsi_xfer *xs;
X	int count;
X{				/* in msec  */
X	bus_chipset_tag_t bc = sc->sc_bc;
X	bus_io_handle_t ioh = sc->sc_ioh;
X
X	while (count) {
X		/*
X		 * If we had interrupts enabled, would we
X		 * have got an interrupt?
X		 */
X		if (bus_io_read_1(bc, ioh, G2STAT) & G2STAT_INT_PEND)
X			ahbintr(sc);
X		if (xs->flags & ITSDONE)
X			return 0;
X		delay(1000);
X		count--;
X	}
X	return 1;
X}
X
Xvoid
Xahb_timeout(arg)
X	void *arg;
X{
X	struct ahb_ecb *ecb = arg;
X	struct scsi_xfer *xs = ecb->xs;
X	struct scsi_link *sc_link = xs->sc_link;
X	struct ahb_softc *sc = sc_link->adapter_softc;
X	int s;
X
X	sc_print_addr(sc_link);
X	printf("timed out");
X
X	s = splbio();
X
X	if (ecb->flags & ECB_IMMED) {
X		printf("\n");
X		ecb->flags |= ECB_IMMED_FAIL;
X		/* XXX Must reset! */
X	} else
X
X	/*
X	 * If it has been through before, then
X	 * a previous abort has failed, don't
X	 * try abort again
X	 */
X	if (ecb->flags & ECB_ABORT) {
X		/* abort timed out */
X		printf(" AGAIN\n");
X		/* XXX Must reset! */
X	} else {
X		/* abort the operation that has timed out */
X		printf("\n");
X		ecb->xs->error = XS_TIMEOUT;
X		ecb->timeout = AHB_ABORT_TIMEOUT;
X		ecb->flags |= ECB_ABORT;
X		ahb_send_mbox(sc, OP_ABORT_ECB, ecb);
X	}
X
X	splx(s);
X}
END-of-ahb.c
echo x - ahbreg.h
sed 's/^X//' >ahbreg.h << 'END-of-ahbreg.h'
X/*	$NetBSD: ahbreg.h,v 1.2 1996/09/01 00:54:34 mycroft Exp $	*/
X
X/*
X * Copyright (c) 1994, 1996 Charles M. Hannum.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by Charles M. Hannum.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Originally written by Julian Elischer (julian@tfs.com)
X * for TRW Financial Systems for use under the MACH(2.5) operating system.
X *
X * TRW Financial Systems, in accordance with their agreement with Carnegie
X * Mellon University, makes this software available to CMU to distribute
X * or use in any manner that they see fit as long as this message is kept with
X * the software. For this reason TFS also grants any other persons or
X * organisations permission to use or modify this software.
X *
X * TFS supplies this software to be publicly redistributed
X * on the understanding that TFS is not responsible for the correct
X * functioning of this software in any circumstances.
X */
X
Xtypedef u_long physaddr;
Xtypedef u_long physlen;
X
X/*
X * AHA1740 EISA board mode registers (Offset from slot base)
X */
X#define PORTADDR	0xCC0
X#define	 PORTADDR_ENHANCED	0x80
X#define BIOSADDR	0xCC1
X#define	INTDEF		0xCC2
X#define	SCSIDEF		0xCC3
X#define	BUSDEF		0xCC4
X#define	RESV0		0xCC5
X#define	RESV1		0xCC6
X#define	RESV2		0xCC7
X/**** bit definitions for INTDEF ****/
X#define	INT9	0x00
X#define	INT10	0x01
X#define	INT11	0x02
X#define	INT12	0x03
X#define	INT14	0x05
X#define	INT15	0x06
X#define INTHIGH 0x08		/* int high=ACTIVE (else edge) */
X#define	INTEN	0x10
X/**** bit definitions for SCSIDEF ****/
X#define	HSCSIID	0x0F		/* our SCSI ID */
X#define	RSTPWR	0x10		/* reset scsi bus on power up or reset */
X/**** bit definitions for BUSDEF ****/
X#define	B0uS	0x00		/* give up bus immediatly */
X#define	B4uS	0x01		/* delay 4uSec. */
X#define	B8uS	0x02
X
X/*
X * AHA1740 ENHANCED mode mailbox control regs (Offset from slot base)
X */
X#define MBOXOUT0	0xCD0
X#define MBOXOUT1	0xCD1
X#define MBOXOUT2	0xCD2
X#define MBOXOUT3	0xCD3
X
X#define	ATTN		0xCD4
X#define	G2CNTRL		0xCD5
X#define	G2INTST		0xCD6
X#define G2STAT		0xCD7
X
X#define	MBOXIN0		0xCD8
X#define	MBOXIN1		0xCD9
X#define	MBOXIN2		0xCDA
X#define	MBOXIN3		0xCDB
X
X#define G2STAT2		0xCDC
X
X/*
X * Bit definitions for the 5 control/status registers
X */
X#define	ATTN_TARGET		0x0F
X#define	ATTN_OPCODE		0xF0
X#define  OP_IMMED		0x10
X#define	  AHB_TARG_RESET	0x80
X#define  OP_START_ECB		0x40
X#define  OP_ABORT_ECB		0x50
X
X#define	G2CNTRL_SET_HOST_READY	0x20
X#define	G2CNTRL_CLEAR_EISA_INT	0x40
X#define	G2CNTRL_HARD_RESET	0x80
X
X#define	G2INTST_TARGET		0x0F
X#define	G2INTST_INT_STAT	0xF0
X#define	 AHB_ECB_OK		0x10
X#define	 AHB_ECB_RECOVERED	0x50
X#define	 AHB_HW_ERR		0x70
X#define	 AHB_IMMED_OK		0xA0
X#define	 AHB_ECB_ERR		0xC0
X#define	 AHB_ASN		0xD0	/* for target mode */
X#define	 AHB_IMMED_ERR		0xE0
X
X#define	G2STAT_BUSY		0x01
X#define	G2STAT_INT_PEND		0x02
X#define	G2STAT_MBOX_EMPTY	0x04
X
X#define	G2STAT2_HOST_READY	0x01
X
X#define	AHB_NSEG	33	/* number of dma segments supported */
X
Xstruct ahb_dma_seg {
X	physaddr seg_addr;
X	physlen seg_len;
X};
X
Xstruct ahb_ecb_status {
X	u_short status;
X#define	ST_DON	0x0001
X#define	ST_DU	0x0002
X#define	ST_QF	0x0008
X#define	ST_SC	0x0010
X#define	ST_DO	0x0020
X#define	ST_CH	0x0040
X#define	ST_INT	0x0080
X#define	ST_ASA	0x0100
X#define	ST_SNS	0x0200
X#define	ST_INI	0x0800
X#define	ST_ME	0x1000
X#define	ST_ECA	0x4000
X	u_char  host_stat;
X#define	HS_OK			0x00
X#define	HS_CMD_ABORTED_HOST	0x04
X#define	HS_CMD_ABORTED_ADAPTER	0x05
X#define	HS_TIMED_OUT		0x11
X#define	HS_HARDWARE_ERR		0x20
X#define	HS_SCSI_RESET_ADAPTER	0x22
X#define	HS_SCSI_RESET_INCOMING	0x23
X	u_char  target_stat;
X	u_long  resid_count;
X	u_long  resid_addr;
X	u_short addit_status;
X	u_char  sense_len;
X	u_char  unused[9];
X	u_char  cdb[6];
X};
X
Xstruct ahb_ecb {
X	u_char  opcode;
X#define	ECB_SCSI_OP	0x01
X	        u_char:4;
X	u_char  options:3;
X	        u_char:1;
X	short   opt1;
X#define	ECB_CNE	0x0001
X#define	ECB_DI	0x0080
X#define	ECB_SES	0x0400
X#define	ECB_S_G	0x1000
X#define	ECB_DSB	0x4000
X#define	ECB_ARS	0x8000
X	short   opt2;
X#define	ECB_LUN	0x0007
X#define	ECB_TAG	0x0008
X#define	ECB_TT	0x0030
X#define	ECB_ND	0x0040
X#define	ECB_DAT	0x0100
X#define	ECB_DIR	0x0200
X#define	ECB_ST	0x0400
X#define	ECB_CHK	0x0800
X#define	ECB_REC	0x4000
X#define	ECB_NRB	0x8000
X	u_short unused1;
X	physaddr data_addr;
X	physlen  data_length;
X	physaddr status;
X	physaddr link_addr;
X	short   unused2;
X	short   unused3;
X	physaddr sense_ptr;
X	u_char  req_sense_length;
X	u_char  scsi_cmd_length;
X	short   cksum;
X	struct scsi_generic scsi_cmd;
X
X	struct ahb_dma_seg ahb_dma[AHB_NSEG];
X	struct ahb_ecb_status ecb_status;
X	struct scsi_sense_data ecb_sense;
X	/*-----------------end of hardware supported fields----------------*/
X	TAILQ_ENTRY(ahb_ecb) chain;
X	struct ahb_ecb *nexthash;
X	long hashkey;
X	struct scsi_xfer *xs;	/* the scsi_xfer for this cmd */
X	int flags;
X#define	ECB_ALLOC	0x01
X#define	ECB_ABORT	0x02
X#define	ECB_IMMED	0x04
X#define	ECB_IMMED_FAIL	0x08
X	int timeout;
X};
END-of-ahbreg.h
echo x - files.eisa
sed 's/^X//' >files.eisa << 'END-of-files.eisa'
X#	$NetBSD: files.eisa,v 1.10.4.1 1996/08/25 17:22:26 thorpej Exp $
X#
X# Config.new file and device description for machine-independent EISA code.
X# Included by ports that need it.  Requires that the SCSI files be
X# defined first.
X
Xdevice	eisa {[slot = -1]}
Xattach	eisa at eisabus
Xfile	dev/eisa/eisa.c			eisa	needs-flag
X
X# Adaptec AHA-174x EISA SCSI Host Adapter family
Xdevice	ahb: scsi
Xattach	ahb at eisa
Xfile	dev/eisa/ahb.c			ahb
X
X# Adaptec AHA-274x and aic7770 motherboard SCSI controllers
X# device declaration in sys/conf/files
Xattach	ahc at eisa with ahc_eisa
Xfile	dev/eisa/ahc_eisa.c		ahc_eisa
X
X# 3Com 3c579 and 3c509 masquerading as EISA Ethernet Controllers
X# device declaration in sys/conf/files
Xattach	ep at eisa with ep_eisa
Xfile	dev/eisa/if_ep_eisa.c		ep_eisa
X
X# DEC DEFEA EISA FDDI Controller
Xdevice	fea: pdq, fddi, ifnet
Xattach	fea at eisa
Xfile	dev/eisa/if_fea.c		fea
END-of-files.eisa
exit