summaryrefslogtreecommitdiff
path: root/scsi/adapters/scsi_aha15_hdw.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /scsi/adapters/scsi_aha15_hdw.c
Initial source
Diffstat (limited to 'scsi/adapters/scsi_aha15_hdw.c')
-rw-r--r--scsi/adapters/scsi_aha15_hdw.c1467
1 files changed, 1467 insertions, 0 deletions
diff --git a/scsi/adapters/scsi_aha15_hdw.c b/scsi/adapters/scsi_aha15_hdw.c
new file mode 100644
index 0000000..5514bc5
--- /dev/null
+++ b/scsi/adapters/scsi_aha15_hdw.c
@@ -0,0 +1,1467 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: scsi_aha15_hdw.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 6/91
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the Adaptec
+ * AHA-15xx family of Intelligent SCSI Host Adapter boards:
+ * probing, start operation, and interrupt routine.
+ */
+
+/*
+ * Since the board is "Intelligent" we do not need scripts like
+ * other simpler HBAs. Maybe.
+ */
+#include <cpus.h>
+#include <platforms.h>
+
+#include <aha.h>
+#if NAHA > 0
+
+#include <mach/std_types.h>
+#include <machine/machspl.h>
+#include <sys/types.h>
+#include <chips/busses.h>
+#include <scsi/compat_30.h>
+
+/* #include <sys/syslog.h> */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+
+#include <scsi/adapters/scsi_aha15.h>
+
+#ifdef AT386
+#define MACHINE_PGBYTES I386_PGBYTES
+#define MAPPABLE 0
+#define gimmeabreak() asm("int3")
+#include <i386/pio.h> /* inlining of outb and inb */
+#endif /*AT386*/
+
+#ifdef CBUS /* For the Corollary machine, physical */
+#include <i386at/mp/mp.h>
+#include <cbus/cbus.h>
+
+#define aha_cbus_window transient_state.hba_dep[0]
+ /* must use windows for phys addresses */
+ /* greater than 16 megs */
+
+#define kvtoAT cbus_kvtoAT
+#else /* CBUS */
+#define kvtoAT kvtophys
+#endif /* CBUS */
+
+#ifndef MACHINE_PGBYTES /* cross compile check */
+#define MACHINE_PGBYTES 0x1000
+#define MAPPABLE 1
+#define gimmeabreak() Debugger("gimmeabreak");
+#endif
+
+/*
+ * Data structures: ring, ccbs, a per target buffer
+ */
+
+#define AHA_NMBOXES 2 /* no need for more, I think */
+struct aha_mb_ctl {
+ aha_mbox_t omb[AHA_NMBOXES];
+ aha_mbox_t imb[AHA_NMBOXES];
+ unsigned char iidx, oidx; /* roving ptrs into */
+};
+#define next_mbx_idx(i) ((((i)+1)==AHA_NMBOXES)?0:((i)+1))
+
+#define AHA_NCCB 8 /* for now */
+struct aha_ccb_raw {
+ target_info_t *active_target;
+ aha_ccb_t ccb;
+ char buffer[256]; /* separate out this ? */
+};
+#define rccb_to_cmdptr(rccb) ((char*)&((rccb)->ccb.ccb_scsi_cmd))
+
+/* forward decls */
+int aha_reset_scsibus();
+boolean_t aha_probe_target();
+
+/*
+ * State descriptor for this layer. There is one such structure
+ * per (enabled) board
+ */
+struct aha_softc {
+ watchdog_t wd;
+ decl_simple_lock_data(, aha_lock)
+ unsigned int port; /* I/O port */
+
+ int ntargets; /* how many alive on this scsibus */
+
+ scsi_softc_t *sc; /* HBA-indep info */
+
+ struct aha_mb_ctl mb; /* mailbox structures */
+
+ /* This chicanery is for mapping back the phys address
+ of a CCB (which we get in an MBI) to its virtual */
+ /* [we could use phystokv(), but it isn't standard] */
+ vm_offset_t I_hold_my_phys_address;
+ struct aha_ccb_raw aha_ccbs[AHA_NCCB];
+
+} aha_softc_data[NAHA];
+
+typedef struct aha_softc *aha_softc_t;
+
+aha_softc_t aha_softc[NAHA];
+
+struct aha_ccb_raw *
+mb_to_rccb(aha, mbi)
+ aha_softc_t aha;
+ aha_mbox_t mbi;
+{
+ vm_offset_t addr;
+
+ AHA_MB_GET_PTR(&mbi,addr); /* phys address of ccb */
+
+ /* make virtual */
+ addr = ((vm_offset_t)&aha->I_hold_my_phys_address) +
+ (addr - aha->I_hold_my_phys_address);
+
+ /* adjust by proper offset to get base */
+ addr -= (vm_offset_t)&(((struct aha_ccb_raw *)0)->ccb);
+
+ return (struct aha_ccb_raw *)addr;
+}
+
+target_info_t *
+aha_tgt_alloc(aha, id, sns_len, tgt)
+ aha_softc_t aha;
+ target_info_t *tgt;
+{
+ struct aha_ccb_raw *rccb;
+
+ aha->ntargets++;
+
+ if (tgt == 0)
+ tgt = scsi_slave_alloc(aha - aha_softc_data, id, aha);
+
+ rccb = &(aha->aha_ccbs[id]);
+ rccb->ccb.ccb_reqsns_len = sns_len;
+ tgt->cmd_ptr = rccb_to_cmdptr(rccb);
+ tgt->dma_ptr = 0;
+#ifdef CBUS
+ tgt->aha_cbus_window = 0;
+#endif /* CBUS */
+ return tgt;
+}
+
+/*
+ * Synch xfer timing conversions
+ */
+#define aha_to_scsi_period(a) ((200 + ((a) * 50)) >> 2)
+#define scsi_period_to_aha(p) ((((p) << 2) - 200) / 50)
+
+/*
+ * Definition of the controller for the auto-configuration program.
+ */
+
+/* DOCUMENTATION */
+/* base ports can be:
+ 0x334, 0x330 (default), 0x234, 0x230, 0x134, 0x130
+ possible interrupt channels are:
+ 9, 10, 11 (default), 12, 14, 15
+ DMA channels can be:
+ 7, 6, 5 (default), 0
+/* DOCUMENTATION */
+
+int aha_probe(), scsi_slave(), aha_go(), aha_intr();
+void scsi_attach();
+
+vm_offset_t aha_std[NAHA] = { 0 };
+struct bus_device *aha_dinfo[NAHA*8];
+struct bus_ctlr *aha_minfo[NAHA];
+struct bus_driver aha_driver =
+ { aha_probe, scsi_slave, scsi_attach, aha_go, aha_std, "rz", aha_dinfo,
+ "ahac", aha_minfo, BUS_INTR_B4_PROBE};
+
+#define DEBUG 1
+#if DEBUG
+
+#define PRINT(x) if (scsi_debug) printf x
+
+aha_state(port)
+{
+ register unsigned char st, intr;
+
+ if (port == 0)
+ port = 0x330;
+ st = inb(AHA_STATUS_PORT(port));
+ intr = inb(AHA_INTR_PORT(port));
+
+ printf("status %x intr %x\n", st, intr);
+ return 0;
+}
+
+aha_target_state(tgt)
+ target_info_t *tgt;
+{
+ if (tgt == 0)
+ tgt = aha_softc[0]->sc->target[0];
+ if (tgt == 0)
+ return 0;
+ printf("fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n",
+ tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd,
+ tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
+ tgt->ior, tgt->done);
+
+ return 0;
+}
+
+aha_all_targets(unit)
+{
+ int i;
+ target_info_t *tgt;
+ for (i = 0; i < 8; i++) {
+ tgt = aha_softc[unit]->sc->target[i];
+ if (tgt)
+ aha_target_state(tgt);
+ }
+}
+
+#define TRMAX 200
+int tr[TRMAX+3];
+int trpt, trpthi;
+#define TR(x) tr[trpt++] = x
+#define TRWRAP trpthi = trpt; trpt = 0;
+#define TRCHECK if (trpt > TRMAX) {TRWRAP}
+
+#define TRACE
+
+#ifdef TRACE
+
+#define LOGSIZE 256
+#define LOG_KERN 0<<3 /* from syslog.h */
+
+int aha_logpt;
+char aha_log[LOGSIZE];
+
+#define MAXLOG_VALUE 0x1e
+struct {
+ char *name;
+ unsigned int count;
+} logtbl[MAXLOG_VALUE];
+
+static LOG(e,f)
+ char *f;
+{
+ aha_log[aha_logpt++] = (e);
+ if (aha_logpt == LOGSIZE) aha_logpt = 0;
+ if ((e) < MAXLOG_VALUE) {
+ logtbl[(e)].name = (f);
+ logtbl[(e)].count++;
+ }
+}
+
+aha_print_log(skip)
+ int skip;
+{
+ register int i, j;
+ register unsigned char c;
+
+ for (i = 0, j = aha_logpt; i < LOGSIZE; i++) {
+ c = aha_log[j];
+ if (++j == LOGSIZE) j = 0;
+ if (skip-- > 0)
+ continue;
+ if (c < MAXLOG_VALUE)
+ printf(" %s", logtbl[c].name);
+ else
+ printf("-%x", c & 0x7f);
+ }
+ return 0;
+}
+
+aha_print_stat()
+{
+ register int i;
+ register char *p;
+ for (i = 0; i < MAXLOG_VALUE; i++) {
+ if (p = logtbl[i].name)
+ printf("%d %s\n", logtbl[i].count, p);
+ }
+}
+
+#else /*TRACE*/
+#define LOG(e,f)
+#define LOGSIZE
+#endif /*TRACE*/
+
+#else /*DEBUG*/
+#define PRINT(x)
+#define LOG(e,f)
+#define LOGSIZE
+#define TRCHECK
+#define TR(a)
+
+#endif /*DEBUG*/
+
+/* Utility functions at end */
+
+
+/*
+ * Probe/Slave/Attach functions
+ */
+
+int aha_dotarget = 1; /* somehow on some boards this is trouble */
+
+/*
+ * Probe routine:
+ * Should find out (a) if the controller is
+ * present and (b) which/where slaves are present.
+ *
+ * Implementation:
+ * Just ask the board to do it
+ */
+aha_probe(port, ui)
+ register port;
+ struct bus_ctlr *ui;
+{
+ int unit = ui->unit;
+ aha_softc_t aha = &aha_softc_data[unit];
+ int target_id;
+ scsi_softc_t *sc;
+ spl_t s;
+ boolean_t did_banner = FALSE;
+ struct aha_devs installed;
+ struct aha_conf conf;
+
+ /* No interrupts yet */
+ s = splbio();
+
+ /*
+ * We should be called with a sensible port, but you never know.
+ * Send an echo command and see that we get it back properly
+ */
+ {
+ register unsigned char st;
+
+ st = inb(AHA_STATUS_PORT(port));
+
+ /*
+ * There is no board reset in case of reboot with
+ * no power-on/power-off sequence. Test it and do
+ * the reset if necessary.
+ */
+
+ if (!(st & AHA_CSR_INIT_REQ)) {
+ outb(AHA_CONTROL_PORT(port),
+ AHA_CTL_SOFT_RESET|AHA_CTL_HARD_RESET);
+ while ((st = inb(AHA_STATUS_PORT(port))) &
+ AHA_CSR_SELF_TEST);
+ }
+ if ((st & AHA_CSR_DATAO_FULL) ||
+ !(st & AHA_CSR_INIT_REQ))
+ goto fail;
+
+ outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO);
+ delay(1000);/*?*/
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL))
+ goto fail;
+
+ outb(AHA_COMMAND_PORT(port), 0x5e);
+ delay(1000);
+
+ st = inb(AHA_STATUS_PORT(port));
+ if ((st & AHA_CSR_CMD_ERR) ||
+ ((st & AHA_CSR_DATAI_FULL) == 0))
+ goto fail;
+
+ st = inb(AHA_DATA_PORT(port));
+ if (st != 0x5e) {
+fail: splx(s);
+ return 0;
+ }
+ /*
+ * augment test with check for echoing inverse and with
+ * test for enhanced adapter with standard ports enabled.
+ */
+
+ /* Check that 0xa1 echoed as well as 0x5e */
+
+ outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO);
+ delay(1000);/*?*/
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL))
+ goto fail;
+
+ outb(AHA_COMMAND_PORT(port), 0xa1);
+ delay(1000);
+
+ st = inb(AHA_STATUS_PORT(port));
+ if ((st & AHA_CSR_CMD_ERR) ||
+ ((st & AHA_CSR_DATAI_FULL) == 0))
+ goto fail;
+
+ st = inb(AHA_DATA_PORT(port));
+ if (st != 0xa1)
+ goto fail ;
+
+ { /* Check that port isn't 174x in enhanced mode
+ with standard mode ports enabled. This should be
+ ignored because it will be caught and correctly
+ handled by eaha_probe(). See TRM4-11..13.
+ dph
+ */
+ unsigned z ;
+ static unsigned port_table[] =
+ {0,0,0x130,0x134,0x230,0x234,0x330,0x334};
+ for (z= 0x1000; z<= 0xF000; z+= 0x1000)
+ if (inb(z+0xC80) == 0x04 &&
+ inb(z+0xC81) == 0x90 &&
+ inb(z+0xCC0) & 0x80 == 0x80 &&
+ port_table [inb(z+0xCC0) & 0x07] == port)
+ goto fail ;
+ }
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR);
+ }
+
+#if MAPPABLE
+ /* Mappable version side */
+ AHA_probe(port, ui);
+#endif /*MAPPABLE*/
+
+ /*
+ * Initialize hw descriptor, cache some pointers
+ */
+ aha_softc[unit] = aha;
+ aha->port = port;
+
+ sc = scsi_master_alloc(unit, aha);
+ aha->sc = sc;
+
+ simple_lock_init(&aha->aha_lock);
+ sc->go = aha_go;
+ sc->watchdog = scsi_watchdog;
+ sc->probe = aha_probe_target;
+ aha->wd.reset = aha_reset_scsibus;
+
+ /* Stupid limitation, no way around it */
+ sc->max_dma_data = (AHA_MAX_SEGLIST-1) * MACHINE_PGBYTES;
+
+
+ /* XXX
+ * I'm not sure how much use this bit of code is really.
+ * On the 1542CF we don't really want to try and initialize
+ * the mailboxes before unlocking them in any case, and
+ * resetting the card is done above.
+ */
+#if 0
+#if 0
+ /*
+ * Reset board.
+ */
+ aha_reset(port, TRUE);
+#else
+ /*
+ * Initialize mailboxes
+ */
+ aha_init_1(aha);
+#endif
+#endif
+
+ /*
+ * Who are we ?
+ */
+ {
+ struct aha_inq inq;
+ struct aha_extbios extbios;
+ char *id;
+
+ aha_command(port, AHA_CMD_INQUIRY, 0, 0, &inq, sizeof(inq), TRUE);
+
+ switch (inq.board_id) {
+ case AHA_BID_1540_B16:
+ case AHA_BID_1540_B64:
+ id = "1540"; break;
+ case AHA_BID_1540B:
+ id = "1540B/1542B"; break;
+ case AHA_BID_1640:
+ id = "1640"; break;
+ case AHA_BID_1740:
+ id = "1740 Unsupported!!"; break;
+ case AHA_BID_1542C:
+ id = "1542C"; aha_dotarget = 0; break;
+ case AHA_BID_1542CF:
+ id = "1542CF"; break;
+ default:
+ id = 0; break;
+ }
+
+ printf("Adaptec %s [id %x], rev %c%c, options x%x\n",
+ id ? id : "Board",
+ inq.board_id, inq.frl_1, inq.frl_2, inq.options);
+
+ /*
+ * If we are a 1542C or 1542CF disable the extended bios
+ * so that the mailbox interface is unlocked.
+ * No need to check the extended bios flags as some of the
+ * extensions that cause us problems are not flagged in
+ * that byte.
+ */
+ if (inq.board_id == 0x44 || inq.board_id == 0x45) {
+ aha_command(port, AHA_EXT_BIOS, 0, 0, &extbios,
+ sizeof(extbios), TRUE);
+#ifdef AHADEBUG
+ printf("aha: extended bios flags 0x%x\n", extbios.flags);
+ printf("aha: mailboxlock 0x%x\n", extbios.mblock);
+#endif /* AHADEBUG */
+
+ printf("aha: 1542C/CF detected, unlocking mailbox\n");
+
+ /* XXX - This sends the mailboxlock code out to the
+ * controller. We need to output a 0, then the
+ * code...so since we don't care about the flags
+ * anyway, we just zero out that field and re-use
+ * the struct.
+ */
+ extbios.flags = 0;
+ aha_command(port, AHA_MBX_ENABLE, &extbios,
+ sizeof(extbios), 0, 0, TRUE);
+ }
+
+ }
+doconf:
+ /*
+ * Readin conf data
+ */
+ aha_command(port, AHA_CMD_GET_CONFIG, 0, 0, &conf, sizeof(conf), TRUE);
+
+ {
+ unsigned char args;
+
+ /*
+ * Change the bus on/off times to not clash with
+ * other dma users.
+ */
+ args = 7;
+ aha_command(port, AHA_CMD_SET_BUSON, &args, 1, 0, 0, TRUE);
+ args = 5;
+ aha_command(port, AHA_CMD_SET_BUSOFF, &args, 1, 0, 0, TRUE);
+ }
+
+ /* XXX - This is _REALLY_ sickening. */
+ /*
+ * Set up the DMA channel we'll be using.
+ */
+ {
+ register int d, i;
+ static struct {
+ unsigned char port;
+ unsigned char init_data;
+ } aha_dma_init[8][2] = {
+ {{0x0b,0x0c}, {0x0a,0x00}}, /* channel 0 */
+ {{0,0},{0,0}},
+ {{0,0},{0,0}},
+ {{0,0},{0,0}},
+ {{0,0},{0,0}},
+ {{0xd6,0xc1}, {0xd4,0x01}}, /* channel 5 (def) */
+ {{0xd6,0xc2}, {0xd4,0x02}}, /* channel 6 */
+ {{0xd6,0xc3}, {0xd4,0x03}} /* channel 7 */
+ };
+
+
+ for (i = 0; i < 8; i++)
+ if ((1 << i) & conf.intr_ch) break;
+ i += 9;
+
+#if there_was_a_way
+ /*
+ * On second unit, avoid clashes with first
+ */
+ if ((unit > 0) && (ui->sysdep1 != i)) {
+ printf("Reprogramming irq and dma ch..\n");
+ ....
+ goto doconf;
+ }
+#endif
+
+ /*
+ * Initialize the DMA controller viz the channel we'll use
+ */
+ for (d = 0; d < 8; d++)
+ if ((1 << d) & conf.dma_arbitration) break;
+
+ outb(aha_dma_init[d][0].port, aha_dma_init[d][0].init_data);
+ outb(aha_dma_init[d][1].port, aha_dma_init[d][1].init_data);
+
+ /* make mapping phys->virt possible for CCBs */
+ aha->I_hold_my_phys_address =
+ kvtoAT((vm_offset_t)&aha->I_hold_my_phys_address);
+
+ /*
+ * Our SCSI ID. (xxx) On some boards this is SW programmable.
+ */
+ sc->initiator_id = conf.my_scsi_id;
+
+ printf("%s%d: [dma ch %d intr ch %d] my SCSI id is %d",
+ ui->name, unit, d, i, sc->initiator_id);
+
+ /* Interrupt vector setup */
+ ui->sysdep1 = i;
+ take_ctlr_irq(ui);
+ }
+
+ /*
+ * More initializations
+ */
+ {
+ register target_info_t *tgt;
+
+ aha_init(aha);
+
+ /* allocate a desc for tgt mode role */
+ tgt = aha_tgt_alloc(aha, sc->initiator_id, 1, 0);
+ sccpu_new_initiator(tgt, tgt); /* self */
+
+ }
+
+ /* Now we could take interrupts, BUT we do not want to
+ be selected as targets by some other host just yet */
+
+ /*
+ * For all possible targets, see if there is one and allocate
+ * a descriptor for it if it is there.
+ * This includes ourselves, when acting as target
+ */
+ aha_command( port, AHA_CMD_FIND_DEVICES, 0, 0, &installed, sizeof(installed), TRUE);
+ for (target_id = 0; target_id < 8; target_id++) {
+
+ if (target_id == sc->initiator_id) /* done already */
+ continue;
+
+ if (installed.tgt_luns[target_id] == 0)
+ continue;
+
+ printf(",%s%d", did_banner++ ? " " : " target(s) at ",
+ target_id);
+
+ /* Normally, only LUN 0 */
+ if (installed.tgt_luns[target_id] != 1)
+ printf("(%x)", installed.tgt_luns[target_id]);
+ /*
+ * Found a target
+ */
+ (void) aha_tgt_alloc(aha, target_id, 1/*no REQSNS*/, 0);
+
+ }
+ printf(".\n");
+ splx(s);
+
+ return 1;
+}
+
+boolean_t
+aha_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+{
+ aha_softc_t aha = aha_softc[tgt->masterno];
+ boolean_t newlywed;
+
+ newlywed = (tgt->cmd_ptr == 0);
+ if (newlywed) {
+ /* desc was allocated afresh */
+ (void) aha_tgt_alloc(aha,tgt->target_id, 1/*no REQSNS*/, tgt);
+ }
+
+ if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
+ return FALSE;
+
+ tgt->flags = TGT_ALIVE;
+ return TRUE;
+}
+
+aha_reset(port, quick)
+{
+ register unsigned char st;
+
+ /*
+ * Reset board and wait till done
+ */
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_SOFT_RESET);
+ do {
+ delay(25);
+ st = inb(AHA_STATUS_PORT(port));
+ } while ((st & (AHA_CSR_IDLE|AHA_CSR_INIT_REQ)) == 0);
+
+ if (quick) return;
+
+ /*
+ * reset the scsi bus. Does NOT generate an interrupt (bozos)
+ */
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_SCSI_RST);
+}
+
+aha_init_1(aha)
+ aha_softc_t aha;
+{
+ struct aha_init a;
+ vm_offset_t phys;
+
+ bzero(&aha->mb, sizeof(aha->mb)); /* also means all free */
+ a.mb_count = AHA_NMBOXES;
+ phys = kvtoAT((vm_offset_t)&aha->mb);
+ AHA_ADDRESS_SET(a.mb_ptr, phys);
+ aha_command(aha->port, AHA_CMD_INIT, &a, sizeof(a), 0, 0, TRUE);
+}
+
+aha_init_2(port)
+{
+ unsigned char disable = AHA_MBO_DISABLE;
+ struct aha_tgt role;
+
+ /* Disable MBO available interrupt */
+ aha_command(port, AHA_CMD_MBO_IE, &disable, 1, 0,0, FALSE);
+
+ if (aha_dotarget) {
+ /* Enable target mode role */
+ role.enable = 1;
+ role.luns = 1; /* only LUN 0 */
+ aha_command(port, AHA_CMD_ENB_TGT_MODE, &role, sizeof(role), 0, 0, TRUE);
+ }
+}
+
+aha_init(aha)
+ aha_softc_t aha;
+{
+ aha_init_1(aha);
+ aha_init_2(aha->port);
+}
+
+/*
+ * Operational functions
+ */
+
+/*
+ * Start a SCSI command on a target
+ */
+aha_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+{
+ aha_softc_t aha;
+ spl_t s;
+ struct aha_ccb_raw *rccb;
+ int len;
+ vm_offset_t virt, phys;
+
+#if CBUS
+ at386_io_lock_state();
+#endif
+
+ LOG(1,"go");
+
+ aha = (aha_softc_t)tgt->hw_state;
+
+/* XXX delay the handling of the ccb till later */
+ rccb = &(aha->aha_ccbs[tgt->target_id]);
+ rccb->active_target = tgt;
+
+ /*
+ * We can do real DMA.
+ */
+/* tgt->transient_state.copy_count = 0; unused */
+/* tgt->transient_state.dma_offset = 0; unused */
+
+ tgt->transient_state.cmd_count = cmd_count;
+
+ if ((tgt->cur_cmd == SCSI_CMD_WRITE) ||
+ (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){
+ io_req_t ior = tgt->ior;
+ register int len = ior->io_count;
+
+ tgt->transient_state.out_count = len;
+
+ /* How do we avoid leaks here ? Trust the board
+ will do zero-padding, for now. XXX CHECKME */
+#if 0
+ if (len < tgt->block_size) {
+ bzero(to + len, tgt->block_size - len);
+ len = tgt->block_size;
+ tgt->transient_state.out_count = len;
+ }
+#endif
+ } else {
+ tgt->transient_state.out_count = 0;
+ }
+
+ /* See above for in_count < block_size */
+ tgt->transient_state.in_count = in_count;
+
+ /*
+ * Setup CCB state
+ */
+ tgt->done = SCSI_RET_IN_PROGRESS;
+
+ switch (tgt->cur_cmd) {
+ case SCSI_CMD_READ:
+ case SCSI_CMD_LONG_READ:
+ LOG(9,"readop");
+ virt = (vm_offset_t)tgt->ior->io_data;
+ len = tgt->transient_state.in_count;
+ rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0;
+ break;
+ case SCSI_CMD_WRITE:
+ case SCSI_CMD_LONG_WRITE:
+ LOG(0x1a,"writeop");
+ virt = (vm_offset_t)tgt->ior->io_data;
+ len = tgt->transient_state.out_count;
+ rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1;
+ break;
+ case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQUEST_SENSE:
+ case SCSI_CMD_MODE_SENSE:
+ case SCSI_CMD_RECEIVE_DIAG_RESULTS:
+ case SCSI_CMD_READ_CAPACITY:
+ case SCSI_CMD_READ_BLOCK_LIMITS:
+ case SCSI_CMD_READ_TOC:
+ case SCSI_CMD_READ_SUBCH:
+ case SCSI_CMD_READ_HEADER:
+ case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */
+ case 0xc6: /* despised: SCSI_CMD_TOSHIBA_READ_SUBCH_Q */
+ case 0xc7: /* despised: SCSI_CMD_TOSHIBA_READ_TOC_ENTRY */
+ case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */
+ case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ virt = (vm_offset_t)tgt->cmd_ptr;
+ len = tgt->transient_state.in_count;
+ rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0;
+ break;
+ case SCSI_CMD_MODE_SELECT:
+ case SCSI_CMD_REASSIGN_BLOCKS:
+ case SCSI_CMD_FORMAT_UNIT:
+ case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */
+ { register int cs = sizeof_scsi_command(tgt->cur_cmd);
+ tgt->transient_state.cmd_count = cs;
+ len =
+ tgt->transient_state.out_count = cmd_count - cs;
+ virt = (vm_offset_t)tgt->cmd_ptr + cs;
+ rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1;
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ }
+ break;
+ default:
+ LOG(0x1c,"cmdop");
+ LOG(0x80+tgt->cur_cmd,0);
+ virt = 0;
+ len = 0;
+ rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 0;
+ }
+
+#if CBUS
+ at386_io_lock(MP_DEV_WAIT);
+#endif
+ aha_prepare_rccb(tgt, rccb, virt, len);
+
+ rccb->ccb.ccb_lun = tgt->lun;
+ rccb->ccb.ccb_scsi_id = tgt->target_id;
+
+/* AHA_LENGTH_SET(rccb->ccb.ccb_linkptr, 0); unused */
+/* rccb->ccb.ccb_linkid = 0; unused */
+
+#if !CBUS
+ s = splbio();
+#endif
+
+ LOG(3,"enqueue");
+
+ aha_start_scsi(aha, &rccb->ccb);
+
+#if CBUS
+ at386_io_unlock();
+#else
+ splx(s);
+#endif
+}
+
+aha_prepare_rccb(tgt, rccb, virt, len)
+ target_info_t *tgt;
+ struct aha_ccb_raw *rccb;
+ vm_offset_t virt;
+ vm_size_t len;
+{
+ vm_offset_t phys;
+#ifdef CBUS
+ int cbus_window;
+#endif /* CBUS */
+
+ rccb->ccb.ccb_cmd_len = tgt->transient_state.cmd_count;
+
+ /* this opcode is refused, grrrr. */
+/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_R; /* default common case */
+ rccb->ccb.ccb_code = AHA_CCB_I_CMD; /* default common case */
+ AHA_LENGTH_SET(rccb->ccb.ccb_datalen, len);/* default common case */
+
+#ifdef CBUS
+ if (tgt->aha_cbus_window == 0)
+ tgt->aha_cbus_window = cbus_alloc_win(AHA_MAX_SEGLIST+1);
+ cbus_window = tgt->aha_cbus_window;
+#endif /* CBUS */
+
+ if (virt == 0) {
+ /* no xfers */
+ AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, 0);
+ } else if (len <= MACHINE_PGBYTES) {
+/* INCORRECT: what if across two pages :INCORRECT */
+ /* simple xfer */
+#ifdef CBUS
+ phys = cbus_kvtoAT_ww(virt, cbus_window);
+#else /* CBUS */
+ phys = kvtophys(virt);
+#endif /* CBUS */
+ AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys);
+ } else {
+ /* messy xfer */
+ aha_seglist_t *seglist;
+ vm_offset_t ph1, off;
+ vm_size_t l1;
+
+ /* this opcode does not work, grrrrr */
+/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG_R;*/
+ rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG;
+
+ if (tgt->dma_ptr == 0)
+ aha_alloc_segment_list(tgt);
+ seglist = (aha_seglist_t *) tgt->dma_ptr;
+#ifdef CBUS
+ phys = cbus_kvtoAT_ww(seglist, cbus_window);
+ cbus_window++;
+#else /* CBUS */
+ phys = kvtophys((vm_offset_t) seglist);
+#endif /* CBUS */
+ AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys);
+
+ ph1 = /*i386_trunc_page*/ virt & ~(MACHINE_PGBYTES - 1);
+ off = virt & (MACHINE_PGBYTES - 1);
+#ifdef CBUS
+ ph1 = cbus_kvtoAT_ww(ph1, cbus_window) + off;
+ cbus_window++;
+#else /* CBUS */
+ ph1 = kvtophys(ph1) + off;
+#endif /* CBUS */
+ l1 = MACHINE_PGBYTES - off;
+
+ off = 1;/* now #pages */
+ while (1) {
+ AHA_ADDRESS_SET(seglist->ptr, ph1);
+ AHA_LENGTH_SET(seglist->len, l1);
+ seglist++;
+
+ if ((len -= l1) <= 0)
+ break;
+ virt += l1; off++;
+
+#ifdef CBUS
+ ph1 = cbus_kvtoAT_ww(virt, cbus_window);
+ cbus_window++;
+#else /* CBUS */
+ ph1 = kvtophys(virt);
+#endif /* CBUS */
+ l1 = (len > MACHINE_PGBYTES) ? MACHINE_PGBYTES : len;
+ }
+ l1 = off * sizeof(*seglist);
+ AHA_LENGTH_SET(rccb->ccb.ccb_datalen, l1);
+ }
+}
+
+aha_start_scsi(aha, ccb)
+ aha_softc_t aha;
+ aha_ccb_t *ccb;
+{
+ register aha_mbox_t *mb;
+ register idx;
+ vm_offset_t phys;
+ aha_mbox_t mbo;
+ spl_t s;
+
+ LOG(4,"start");
+ LOG(0x80+ccb->ccb_scsi_id,0);
+
+ /*
+ * Get an MBO, spin if necessary (takes little time)
+ */
+ s = splbio();
+ phys = kvtoAT((vm_offset_t)ccb);
+ /* might cross pages, but should be ok (kernel is contig) */
+ AHA_MB_SET_PTR(&mbo,phys);
+ mbo.mb.mb_cmd = AHA_MBO_START;
+
+ simple_lock(&aha->aha_lock);
+ if (aha->wd.nactive++ == 0)
+ aha->wd.watchdog_state = SCSI_WD_ACTIVE;
+ idx = aha->mb.oidx;
+ aha->mb.oidx = next_mbx_idx(idx);
+ mb = &aha->mb.omb[idx];
+ while (mb->mb.mb_status != AHA_MBO_FREE)
+ delay(1);
+ mb->bits = mbo.bits;
+ simple_unlock(&aha->aha_lock);
+
+ /*
+ * Start the board going
+ */
+ aha_command(aha->port, AHA_CMD_START, 0, 0, 0, 0, FALSE);
+ splx(s);
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the board
+ *
+ * Implementation:
+ * TBD
+ */
+aha_intr(unit)
+{
+ register aha_softc_t aha;
+ register port;
+ register csr, intr;
+#if MAPPABLE
+ extern boolean_t rz_use_mapped_interface;
+
+ if (rz_use_mapped_interface)
+ return AHA_intr(unit);
+#endif /*MAPPABLE*/
+
+ aha = aha_softc[unit];
+ port = aha->port;
+
+ LOG(5,"\n\tintr");
+gotintr:
+ /* collect ephemeral information */
+ csr = inb(AHA_STATUS_PORT(port));
+ intr = inb(AHA_INTR_PORT(port));
+
+ /*
+ * Check for errors
+ */
+ if (csr & (AHA_CSR_DIAG_FAIL|AHA_CSR_CMD_ERR)) {
+/* XXX */ gimmeabreak();
+ }
+
+ /* drop spurious interrupts */
+ if ((intr & AHA_INTR_PENDING) == 0) {
+ LOG(2,"SPURIOUS");
+ return;
+ }
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR);
+
+TR(csr);TR(intr);TRCHECK
+
+ if (intr & AHA_INTR_RST)
+ return aha_bus_reset(aha);
+
+ /* we got an interrupt allright */
+ if (aha->wd.nactive)
+ aha->wd.watchdog_state = SCSI_WD_ACTIVE;
+
+ if (intr == AHA_INTR_DONE) {
+ /* csr & AHA_CSR_CMD_ERR --> with error */
+ LOG(6,"done");
+ return;
+ }
+
+/* if (intr & AHA_INTR_MBO_AVAIL) will not happen */
+
+ /* Some real work today ? */
+ if (intr & AHA_INTR_MBI_FULL) {
+ register int idx;
+ register aha_mbox_t *mb;
+ int nscan = 0;
+ aha_mbox_t mbi;
+rescan:
+ simple_lock(&aha->aha_lock);
+ idx = aha->mb.iidx;
+ aha->mb.iidx = next_mbx_idx(idx);
+ mb = &aha->mb.imb[idx];
+ mbi.bits = mb->bits;
+ mb->mb.mb_status = AHA_MBI_FREE;
+ simple_unlock(&aha->aha_lock);
+
+ nscan++;
+
+ switch (mbi.mb.mb_status) {
+
+ case AHA_MBI_FREE:
+ if (nscan >= AHA_NMBOXES)
+ return;
+ goto rescan;
+ break;
+
+ case AHA_MBI_SUCCESS:
+ case AHA_MBI_ERROR:
+ aha_initiator_intr(aha, mbi);
+ break;
+
+ case AHA_MBI_NEED_CCB:
+ aha_target_intr(aha, mbi);
+ break;
+
+/* case AHA_MBI_ABORTED: /* this we wont see */
+/* case AHA_MBI_NOT_FOUND: /* this we wont see */
+ default:
+ log( LOG_KERN,
+ "aha%d: Bogus status (x%x) in MBI\n",
+ unit, mbi.mb.mb_status);
+ break;
+ }
+
+ /* peek ahead */
+ if (aha->mb.imb[aha->mb.iidx].mb.mb_status != AHA_MBI_FREE)
+ goto rescan;
+ }
+
+ /* See if more work ready */
+ if (inb(AHA_INTR_PORT(port)) & AHA_INTR_PENDING) {
+ LOG(7,"\n\tre-intr");
+ goto gotintr;
+ }
+}
+
+/*
+ * The interrupt routine turns to one of these two
+ * functions, depending on the incoming mbi's role
+ */
+aha_target_intr(aha, mbi)
+ aha_softc_t aha;
+ aha_mbox_t mbi;
+{
+ target_info_t *initiator; /* this is the caller */
+ target_info_t *self; /* this is us */
+ int len;
+
+ if (mbi.mbt.mb_cmd != AHA_MBI_NEED_CCB)
+ gimmeabreak();
+
+ /* If we got here this is not zero .. */
+ self = aha->sc->target[aha->sc->initiator_id];
+
+ initiator = aha->sc->target[mbi.mbt.mb_initiator_id];
+ /* ..but initiators are not required to answer to our inquiry */
+ if (initiator == 0) {
+ /* allocate */
+ initiator = aha_tgt_alloc(aha, mbi.mbt.mb_initiator_id,
+ sizeof(scsi_sense_data_t) + 5, 0);
+
+ /* We do not know here wether the host was down when
+ we inquired, or it refused the connection. Leave
+ the decision on how we will talk to it to higher
+ level code */
+ LOG(0xC, "new_initiator");
+ sccpu_new_initiator(self, initiator);
+ }
+
+ /* The right thing to do would be build an ior
+ and call the self->dev_ops->strategy routine,
+ but we cannot allocate it at interrupt level.
+ Also note that we are now disconnected from the
+ initiator, no way to do anything else with it
+ but reconnect and do what it wants us to do */
+
+ /* obviously, this needs both spl and MP protection */
+ self->dev_info.cpu.req_pending = TRUE;
+ self->dev_info.cpu.req_id = mbi.mbt.mb_initiator_id;
+ self->dev_info.cpu.req_lun = mbi.mbt.mb_lun;
+ self->dev_info.cpu.req_cmd =
+ mbi.mbt.mb_isa_send ? SCSI_CMD_SEND: SCSI_CMD_RECEIVE;
+ len = (mbi.mbt.mb_data_len_msb << 16) |
+ (mbi.mbt.mb_data_len_mid << 8 );
+ len += 0x100;/* truncation problem */
+ self->dev_info.cpu.req_len = len;
+
+ LOG(0xB,"tgt-mode-restart");
+ (*self->dev_ops->restart)( self, FALSE);
+
+ /* The call above has either prepared the data,
+ placing an ior on self, or it handled it some
+ other way */
+ if (self->ior == 0)
+ return; /* I guess we'll do it later */
+
+ {
+ struct aha_ccb_raw *rccb;
+
+ rccb = &(aha->aha_ccbs[initiator->target_id]);
+ rccb->active_target = initiator;
+ if (self->dev_info.cpu.req_cmd == SCSI_CMD_SEND) {
+ rccb->ccb.ccb_in = 1;
+ rccb->ccb.ccb_out = 0;
+ } else {
+ rccb->ccb.ccb_in = 0;
+ rccb->ccb.ccb_out = 1;
+ }
+
+ aha_prepare_rccb(initiator, rccb,
+ (vm_offset_t)self->ior->io_data, self->ior->io_count);
+ rccb->ccb.ccb_code = AHA_CCB_T_CMD;
+ rccb->ccb.ccb_lun = initiator->lun;
+ rccb->ccb.ccb_scsi_id = initiator->target_id;
+
+ simple_lock(&aha->aha_lock);
+ if (aha->wd.nactive++ == 0)
+ aha->wd.watchdog_state = SCSI_WD_ACTIVE;
+ simple_unlock(&aha->aha_lock);
+
+ aha_start_scsi(aha, &rccb->ccb);
+ }
+}
+
+aha_initiator_intr(aha, mbi)
+ aha_softc_t aha;
+ aha_mbox_t mbi;
+{
+ struct aha_ccb_raw *rccb;
+ scsi2_status_byte_t status;
+ target_info_t *tgt;
+
+ rccb = mb_to_rccb(aha,mbi);
+ tgt = rccb->active_target;
+ rccb->active_target = 0;
+
+ /* shortcut (sic!) */
+ if (mbi.mb.mb_status == AHA_MBI_SUCCESS)
+ goto allok;
+
+ switch (rccb->ccb.ccb_hstatus) {
+ case AHA_HST_SUCCESS:
+allok:
+ status = rccb->ccb.ccb_status;
+ if (status.st.scsi_status_code != SCSI_ST_GOOD) {
+ scsi_error(tgt, SCSI_ERR_STATUS, status.bits, 0);
+ tgt->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
+ SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
+ } else
+ tgt->done = SCSI_RET_SUCCESS;
+ break;
+ case AHA_HST_SEL_TIMEO:
+ if (tgt->flags & TGT_FULLY_PROBED)
+ tgt->flags = 0; /* went offline */
+ tgt->done = SCSI_RET_DEVICE_DOWN;
+ break;
+ case AHA_HST_DATA_OVRUN:
+ /* BUT we don't know if this is an underrun.
+ It is ok if we get less data than we asked
+ for, in a number of cases. Most boards do not
+ seem to generate this anyways, but some do. */
+ { register int cmd = tgt->cur_cmd;
+ switch (cmd) {
+ case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQUEST_SENSE:
+ break;
+ default:
+ printf("%sx%x\n",
+ "aha: U/OVRUN on scsi command x%x\n",
+ cmd);
+ gimmeabreak();
+ }
+ }
+ goto allok;
+ case AHA_HST_BAD_DISCONN:
+ printf("aha: bad disconnect\n");
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ case AHA_HST_BAD_PHASE_SEQ:
+ /* we'll get an interrupt soon */
+ printf("aha: bad PHASE sequencing\n");
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ case AHA_HST_BAD_OPCODE: /* fall through */
+ case AHA_HST_BAD_PARAM:
+printf("aha: BADCCB\n");gimmeabreak();
+ tgt->done = SCSI_RET_RETRY;
+ break;
+ case AHA_HST_BAD_LINK_LUN: /* these should not happen */
+ case AHA_HST_INVALID_TDIR:
+ case AHA_HST_DUPLICATED_CCB:
+ printf("aha: bad hstatus (x%x)\n", rccb->ccb.ccb_hstatus);
+ tgt->done = SCSI_RET_ABORTED;
+ break;
+ }
+
+ LOG(8,"end");
+
+ simple_lock(&aha->aha_lock);
+ if (aha->wd.nactive-- == 1)
+ aha->wd.watchdog_state = SCSI_WD_INACTIVE;
+ simple_unlock(&aha->aha_lock);
+
+ if (tgt->ior) {
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ }
+
+ return FALSE;
+}
+
+/*
+ * The bus was reset
+ */
+aha_bus_reset(aha)
+ register aha_softc_t aha;
+{
+ register port = aha->port;
+
+ LOG(0x1d,"bus_reset");
+
+ /*
+ * Clear bus descriptor
+ */
+ aha->wd.nactive = 0;
+ aha_reset(port, TRUE);
+ aha_init(aha);
+
+ printf("aha: (%d) bus reset ", ++aha->wd.reset_count);
+ delay(scsi_delay_after_reset); /* some targets take long to reset */
+
+ if (aha->sc == 0) /* sanity */
+ return;
+
+ scsi_bus_was_reset(aha->sc);
+}
+
+/*
+ * Watchdog
+ *
+ * We know that some (name withdrawn) disks get
+ * stuck in the middle of dma phases...
+ */
+aha_reset_scsibus(aha)
+ register aha_softc_t aha;
+{
+ register target_info_t *tgt;
+ register port = aha->port;
+ register int i;
+
+ for (i = 0; i < AHA_NCCB; i++) {
+ tgt = aha->aha_ccbs[i].active_target;
+ if (/*scsi_debug &&*/ tgt)
+ printf("Target %d was active, cmd x%x in x%x out x%x\n",
+ tgt->target_id, tgt->cur_cmd,
+ tgt->transient_state.in_count,
+ tgt->transient_state.out_count);
+ }
+ aha_reset(port, FALSE);
+ delay(35);
+ /* no interrupt will come */
+ aha_bus_reset(aha);
+}
+
+/*
+ * Utilities
+ */
+
+/*
+ * Send a command to the board along with some
+ * optional parameters, optionally receive the
+ * results at command completion, returns how
+ * many bytes we did NOT get back.
+ */
+aha_command(port, cmd, outp, outc, inp, inc, clear_interrupt)
+ unsigned char *outp, *inp;
+{
+ register unsigned char st;
+ boolean_t failed = TRUE;
+
+ do {
+ st = inb(AHA_STATUS_PORT(port));
+ } while (st & AHA_CSR_DATAO_FULL);
+
+ /* Output command and any data */
+ outb(AHA_COMMAND_PORT(port), cmd);
+ while (outc--) {
+ do {
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & AHA_CSR_CMD_ERR) goto out;
+ } while (st & AHA_CSR_DATAO_FULL);
+
+ outb(AHA_COMMAND_PORT(port), *outp++);
+ }
+
+ /* get any data */
+ while (inc--) {
+ do {
+ st = inb(AHA_STATUS_PORT(port));
+ if (st & AHA_CSR_CMD_ERR) goto out;
+ } while ((st & AHA_CSR_DATAI_FULL) == 0);
+
+ *inp++ = inb(AHA_DATA_PORT(port));
+ }
+ ++inc;
+ failed = FALSE;
+
+ /* wait command complete */
+ if (clear_interrupt) do {
+ delay(1);
+ st = inb(AHA_INTR_PORT(port));
+ } while ((st & AHA_INTR_DONE) == 0);
+
+out:
+ if (clear_interrupt)
+ outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR);
+ if (failed)
+ printf("aha_command: error on (%x %x %x %x %x %x), status %x\n",
+ port, cmd, outp, outc, inp, inc, st);
+ return inc;
+}
+
+#include <vm/vm_kern.h>
+
+/*
+ * Allocate dynamically segment lists to
+ * targets (for scatter/gather)
+ * Its a max of 17*6=102 bytes per target.
+ */
+vm_offset_t aha_seglist_next, aha_seglist_end;
+
+aha_alloc_segment_list(tgt)
+ target_info_t *tgt;
+{
+#define ALLOC_SIZE (AHA_MAX_SEGLIST * sizeof(aha_seglist_t))
+
+/* XXX locking */
+ if ((aha_seglist_next + ALLOC_SIZE) > aha_seglist_end) {
+ (void) kmem_alloc_wired(kernel_map, &aha_seglist_next, PAGE_SIZE);
+ aha_seglist_end = aha_seglist_next + PAGE_SIZE;
+ }
+ tgt->dma_ptr = (char *)aha_seglist_next;
+ aha_seglist_next += ALLOC_SIZE;
+/* XXX locking */
+}
+
+#endif /* NAHA > 0 */
+