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: current-users
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