summaryrefslogtreecommitdiff
path: root/sys/dev/tc/asc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/tc/asc.c')
-rw-r--r--sys/dev/tc/asc.c2203
1 files changed, 2200 insertions, 3 deletions
diff --git a/sys/dev/tc/asc.c b/sys/dev/tc/asc.c
index c703e91024a..d41fc46f32b 100644
--- a/sys/dev/tc/asc.c
+++ b/sys/dev/tc/asc.c
@@ -1,3 +1,2200 @@
-d1 1
-a1 1
-/* $NetBSD: asc.c,v 1.15 1995/09/13 19:35:53 jonathan Exp $ */
+/* $NetBSD: asc.c,v 1.16 1996/01/04 17:43:23 jonathan Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell and Rick Macklem.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)asc.c 8.3 (Berkeley) 7/3/94
+ */
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 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
+ * 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 the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * Log: scsi_53C94_hdw.c,v
+ * Revision 2.5 91/02/05 17:45:07 mrt
+ * Added author notices
+ * [91/02/04 11:18:43 mrt]
+ *
+ * Changed to use new Mach copyright
+ * [91/02/02 12:17:20 mrt]
+ *
+ * Revision 2.4 91/01/08 15:48:24 rpd
+ * Added continuation argument to thread_block.
+ * [90/12/27 rpd]
+ *
+ * Revision 2.3 90/12/05 23:34:48 af
+ * Recovered from pmax merge.. and from the destruction of a disk.
+ * [90/12/03 23:40:40 af]
+ *
+ * Revision 2.1.1.1 90/11/01 03:39:09 af
+ * Created, from the DEC specs:
+ * "PMAZ-AA TURBOchannel SCSI Module Functional Specification"
+ * Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990.
+ * And from the NCR data sheets
+ * "NCR 53C94, 53C95, 53C96 Advances SCSI Controller"
+ * [90/09/03 af]
+ */
+
+/*
+ * File: scsi_53C94_hdw.h
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 9/90
+ *
+ * Bottom layer of the SCSI driver: chip-dependent functions
+ *
+ * This file contains the code that is specific to the NCR 53C94
+ * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
+ * operation, and interrupt routine.
+ */
+
+/*
+ * This layer works based on small simple 'scripts' that are installed
+ * at the start of the command and drive the chip to completion.
+ * The idea comes from the specs of the NCR 53C700 'script' processor.
+ *
+ * There are various reasons for this, mainly
+ * - Performance: identify the common (successful) path, and follow it;
+ * at interrupt time no code is needed to find the current status
+ * - Code size: it should be easy to compact common operations
+ * - Adaptability: the code skeleton should adapt to different chips without
+ * terrible complications.
+ * - Error handling: and it is easy to modify the actions performed
+ * by the scripts to cope with strange but well identified sequences
+ *
+ */
+
+#include <asc.h>
+#if NASC > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#include <machine/machConst.h>
+#include <machine/autoconf.h>
+
+#include <pmax/dev/device.h>
+#include <pmax/dev/scsi.h>
+#include <pmax/dev/ascreg.h>
+
+#include <pmax/pmax/asic.h>
+#include <pmax/pmax/kmin.h>
+#include <pmax/pmax/pmaxtype.h>
+
+
+#define readback(a) { register int foo; foo = (a); }
+extern int pmax_boardtype;
+
+/*
+ * In 4ns ticks.
+ */
+int asc_to_scsi_period[] = {
+ 32,
+ 33,
+ 34,
+ 35,
+ 5,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 31,
+};
+
+/*
+ * Internal forward declarations.
+ */
+static void asc_reset();
+static void asc_startcmd();
+
+#ifdef DEBUG
+int asc_debug = 1;
+int asc_debug_cmd;
+int asc_debug_bn;
+int asc_debug_sz;
+#define NLOG 32
+struct asc_log {
+ u_int status;
+ u_char state;
+ u_char msg;
+ int target;
+ int resid;
+} asc_log[NLOG], *asc_logp = asc_log;
+#define PACK(unit, status, ss, ir) \
+ ((unit << 24) | (status << 16) | (ss << 8) | ir)
+#endif
+
+/*
+ * Scripts are entries in a state machine table.
+ * A script has four parts: a pre-condition, an action, a command to the chip,
+ * and an index into asc_scripts for the next state. The first triggers error
+ * handling if not satisfied and in our case it is formed by the
+ * values of the interrupt register and status register, this
+ * basically captures the phase of the bus and the TC and BS
+ * bits. The action part is just a function pointer, and the
+ * command is what the 53C94 should be told to do at the end
+ * of the action processing. This command is only issued and the
+ * script proceeds if the action routine returns TRUE.
+ * See asc_intr() for how and where this is all done.
+ */
+typedef struct script {
+ int condition; /* expected state at interrupt time */
+ int (*action)(); /* extra operations */
+ int command; /* command to the chip */
+ struct script *next; /* index into asc_scripts for next state */
+} script_t;
+
+/* Matching on the condition value */
+#define SCRIPT_MATCH(ir, csr) ((ir) | (((csr) & 0x67) << 8))
+
+/* forward decls of script actions */
+static int script_nop(); /* when nothing needed */
+static int asc_end(); /* all come to an end */
+static int asc_get_status(); /* get status from target */
+static int asc_dma_in(); /* start reading data from target */
+static int asc_last_dma_in(); /* cleanup after all data is read */
+static int asc_resume_in(); /* resume data in after a message */
+static int asc_resume_dma_in(); /* resume DMA after a disconnect */
+static int asc_dma_out(); /* send data to target via dma */
+static int asc_last_dma_out(); /* cleanup after all data is written */
+static int asc_resume_out(); /* resume data out after a message */
+static int asc_resume_dma_out(); /* resume DMA after a disconnect */
+static int asc_sendsync(); /* negotiate sync xfer */
+static int asc_replysync(); /* negotiate sync xfer */
+static int asc_msg_in(); /* process a message byte */
+static int asc_disconnect(); /* process an expected disconnect */
+
+/* Define the index into asc_scripts for various state transitions */
+#define SCRIPT_DATA_IN 0
+#define SCRIPT_CONTINUE_IN 2
+#define SCRIPT_DATA_OUT 3
+#define SCRIPT_CONTINUE_OUT 5
+#define SCRIPT_SIMPLE 6
+#define SCRIPT_GET_STATUS 7
+#define SCRIPT_DONE 8
+#define SCRIPT_MSG_IN 9
+#define SCRIPT_REPLY_SYNC 11
+#define SCRIPT_TRY_SYNC 12
+#define SCRIPT_DISCONNECT 15
+#define SCRIPT_RESEL 16
+#define SCRIPT_RESUME_IN 17
+#define SCRIPT_RESUME_DMA_IN 18
+#define SCRIPT_RESUME_OUT 19
+#define SCRIPT_RESUME_DMA_OUT 20
+#define SCRIPT_RESUME_NO_DATA 21
+
+/*
+ * Scripts
+ */
+script_t asc_scripts[] = {
+ /* start data in */
+ {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAI), /* 0 */
+ asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_IN + 1]},
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 1 */
+ asc_last_dma_in, ASC_CMD_I_COMPLETE,
+ &asc_scripts[SCRIPT_GET_STATUS]},
+
+ /* continue data in after a chunk is finished */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 2 */
+ asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_IN + 1]},
+
+ /* start data out */
+ {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAO), /* 3 */
+ asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_OUT + 1]},
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 4 */
+ asc_last_dma_out, ASC_CMD_I_COMPLETE,
+ &asc_scripts[SCRIPT_GET_STATUS]},
+
+ /* continue data out after a chunk is finished */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 5 */
+ asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_OUT + 1]},
+
+ /* simple command with no data transfer */
+ {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_STATUS), /* 6 */
+ script_nop, ASC_CMD_I_COMPLETE,
+ &asc_scripts[SCRIPT_GET_STATUS]},
+
+ /* get status and finish command */
+ {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 7 */
+ asc_get_status, ASC_CMD_MSG_ACPT,
+ &asc_scripts[SCRIPT_DONE]},
+ {SCRIPT_MATCH(ASC_INT_DISC, 0), /* 8 */
+ asc_end, ASC_CMD_NOP,
+ &asc_scripts[SCRIPT_DONE]},
+
+ /* message in */
+ {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 9 */
+ asc_msg_in, ASC_CMD_MSG_ACPT,
+ &asc_scripts[SCRIPT_MSG_IN + 1]},
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 10 */
+ script_nop, ASC_CMD_XFER_INFO,
+ &asc_scripts[SCRIPT_MSG_IN]},
+
+ /* send synchonous negotiation reply */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 11 */
+ asc_replysync, ASC_CMD_XFER_INFO,
+ &asc_scripts[SCRIPT_REPLY_SYNC]},
+
+ /* try to negotiate synchonous transfer parameters */
+ {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 12 */
+ asc_sendsync, ASC_CMD_XFER_INFO,
+ &asc_scripts[SCRIPT_TRY_SYNC + 1]},
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 13 */
+ script_nop, ASC_CMD_XFER_INFO,
+ &asc_scripts[SCRIPT_MSG_IN]},
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_COMMAND), /* 14 */
+ script_nop, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_RESUME_NO_DATA]},
+
+ /* handle a disconnect */
+ {SCRIPT_MATCH(ASC_INT_DISC, ASC_PHASE_DATAO), /* 15 */
+ asc_disconnect, ASC_CMD_ENABLE_SEL,
+ &asc_scripts[SCRIPT_RESEL]},
+
+ /* reselect sequence: this is just a placeholder so match fails */
+ {SCRIPT_MATCH(0, ASC_PHASE_MSG_IN), /* 16 */
+ script_nop, ASC_CMD_MSG_ACPT,
+ &asc_scripts[SCRIPT_RESEL]},
+
+ /* resume data in after a message */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 17 */
+ asc_resume_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_IN + 1]},
+
+ /* resume partial DMA data in after a message */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 18 */
+ asc_resume_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_IN + 1]},
+
+ /* resume data out after a message */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 19 */
+ asc_resume_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_OUT + 1]},
+
+ /* resume partial DMA data out after a message */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 20 */
+ asc_resume_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA,
+ &asc_scripts[SCRIPT_DATA_OUT + 1]},
+
+ /* resume after a message when there is no more data */
+ {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 21 */
+ script_nop, ASC_CMD_I_COMPLETE,
+ &asc_scripts[SCRIPT_GET_STATUS]},
+};
+
+/*
+ * State kept for each active SCSI device.
+ */
+typedef struct scsi_state {
+ script_t *script; /* saved script while processing error */
+ int statusByte; /* status byte returned during STATUS_PHASE */
+ int error; /* errno to pass back to device driver */
+ u_char *dmaBufAddr; /* DMA buffer address */
+ u_int dmaBufSize; /* DMA buffer size */
+ int dmalen; /* amount to transfer in this chunk */
+ int dmaresid; /* amount not transfered if chunk suspended */
+ int buflen; /* total remaining amount of data to transfer */
+ char *buf; /* current pointer within scsicmd->buf */
+ int flags; /* see below */
+ int msglen; /* number of message bytes to read */
+ int msgcnt; /* number of message bytes received */
+ u_char sync_period; /* DMA synchronous period */
+ u_char sync_offset; /* DMA synchronous xfer offset or 0 if async */
+ u_char msg_out; /* next MSG_OUT byte to send */
+ u_char msg_in[16]; /* buffer for multibyte messages */
+} State;
+
+/* state flags */
+#define DISCONN 0x001 /* true if currently disconnected from bus */
+#define DMA_IN_PROGRESS 0x002 /* true if data DMA started */
+#define DMA_IN 0x004 /* true if reading from SCSI device */
+#define DMA_OUT 0x010 /* true if writing to SCSI device */
+#define DID_SYNC 0x020 /* true if synchronous offset was negotiated */
+#define TRY_SYNC 0x040 /* true if try neg. synchronous offset */
+#define PARITY_ERR 0x080 /* true if parity error seen */
+#define CHECK_SENSE 0x100 /* true if doing sense command */
+
+/*
+ * State kept for each active SCSI host interface (53C94).
+ */
+struct asc_softc {
+ struct device sc_dev; /* us as a device */
+ asc_regmap_t *regs; /* chip address */
+ volatile int *dmar; /* DMA address register address */
+ u_char *buff; /* RAM buffer address (uncached) */
+ int sc_id; /* SCSI ID of this interface */
+ int myidmask; /* ~(1 << myid) */
+ int state; /* current SCSI connection state */
+ int target; /* target SCSI ID if busy */
+ script_t *script; /* next expected interrupt & action */
+ ScsiCmd *cmd[ASC_NCMD]; /* active command indexed by SCSI ID */
+ State st[ASC_NCMD]; /* state info for each active command */
+ void (*dma_start)(); /* Start dma routine */
+ void (*dma_end)(); /* End dma routine */
+ u_char *dma_next;
+ int dma_xfer; /* Dma len still to go */
+ int min_period; /* Min transfer period clk/byte */
+ int max_period; /* Max transfer period clk/byte */
+ int ccf; /* CCF, whatever that really is? */
+ int timeout_250; /* 250ms timeout */
+ int tb_ticks; /* 4ns. ticks/tb channel ticks */
+#ifdef USE_NEW_SCSI
+ struct scsi_link sc_link; /* scsi link struct */
+#endif
+};
+
+#define ASC_STATE_IDLE 0 /* idle state */
+#define ASC_STATE_BUSY 1 /* selecting or currently connected */
+#define ASC_STATE_TARGET 2 /* currently selected as target */
+#define ASC_STATE_RESEL 3 /* currently waiting for reselect */
+
+typedef struct asc_softc *asc_softc_t;
+
+/*
+ * Dma operations.
+ */
+#define ASCDMA_READ 1
+#define ASCDMA_WRITE 2
+static void tb_dma_start(), tb_dma_end(), asic_dma_start(), asic_dma_end();
+extern u_long asc_iomem;
+extern u_long asic_base;
+
+/*
+ * Autoconfiguration data for config.
+ */
+int ascmatch __P((struct device * parent, void *cfdata, void *aux));
+void ascattach __P((struct device *parent, struct device *self, void *aux));
+int ascprint(void*, char*);
+
+extern struct cfdriver asccd;
+struct cfdriver asccd = {
+ NULL, "asc", ascmatch, ascattach, DV_DULL, sizeof(struct asc_softc)
+};
+
+#ifdef USE_NEW_SCSI
+/* Glue to the machine-independent scsi */
+struct scsi_adapter asc_switch = {
+ NULL, /* XXX - asc_scsi_cmd */
+/*XXX*/ minphys, /* no max transfer size; DMA engine deals */
+ NULL,
+ NULL,
+};
+
+struct scsi_device asc_dev = {
+/*XXX*/ NULL, /* Use default error handler */
+/*XXX*/ NULL, /* have a queue, served by this */
+/*XXX*/ NULL, /* have no async handler */
+/*XXX*/ NULL, /* Use default 'done' routine */
+};
+#endif
+
+/*
+ * Definition of the controller for the old auto-configuration program.
+ */
+void asc_start();
+int asc_intr __P ((void *asc));
+struct pmax_driver ascdriver = {
+ "asc", NULL, asc_start, 0, asc_intr,
+};
+
+
+/*
+ * Match driver based on name
+ */
+int
+ascmatch(parent, match, aux)
+ struct device *parent;
+ void *match;
+ void *aux;
+{
+ struct cfdata *cf = match;
+ struct confargs *ca = aux;
+ void *sccaddr;
+
+ if (!BUS_MATCHNAME(ca, "asc") && !BUS_MATCHNAME(ca, "PMAZ-AA "))
+ return (0);
+
+ sccaddr = BUS_CVTADDR(ca);
+
+ if (badaddr(sccaddr + ASC_OFFSET_53C94, 4))
+ return (0);
+
+ return (1);
+}
+
+void
+ascattach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ register struct confargs *ca = aux;
+ register asc_softc_t asc = (asc_softc_t) self;
+ register asc_regmap_t *regs;
+ int id, s, i;
+ int bufsiz;
+
+ void *ascaddr;
+ int unit;
+
+ ascaddr = (void *)MACH_PHYS_TO_UNCACHED(BUS_CVTADDR(ca));
+ unit = asc->sc_dev.dv_unit;
+
+ /*
+ * Initialize hw descriptor, cache some pointers
+ */
+ asc->regs = (asc_regmap_t *)(ascaddr + ASC_OFFSET_53C94);
+
+ /*
+ * Set up machine dependencies.
+ * (1) how to do dma
+ * (2) timing based on turbochannel frequency
+ */
+ switch (pmax_boardtype) {
+ case DS_3MIN:
+ case DS_MAXINE:
+ case DS_3MAXPLUS:
+ if (unit == 0) {
+ asc->buff = (u_char *)MACH_PHYS_TO_UNCACHED(asc_iomem);
+ bufsiz = 8192;
+ *((volatile int *)ASIC_REG_SCSI_DMAPTR(asic_base)) = -1;
+ *((volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base)) = -1;
+ *((volatile int *)ASIC_REG_SCSI_SCR(asic_base)) = 0;
+ asc->dma_start = asic_dma_start;
+ asc->dma_end = asic_dma_end;
+ break;
+ }
+ /*
+ * Fall through for turbochannel option.
+ */
+ case DS_3MAX:
+ default:
+ asc->dmar = (volatile int *)(ascaddr + ASC_OFFSET_DMAR);
+ asc->buff = (u_char *)(ascaddr + ASC_OFFSET_RAM);
+ bufsiz = PER_TGT_DMA_SIZE;
+ asc->dma_start = tb_dma_start;
+ asc->dma_end = tb_dma_end;
+ };
+ /*
+ * Now for timing. The 3max has a 25Mhz tb whereas the 3min and
+ * maxine are 12.5Mhz.
+ */
+ switch (pmax_boardtype) {
+ case DS_3MAX:
+ case DS_3MAXPLUS:
+ asc->min_period = ASC_MIN_PERIOD25;
+ asc->max_period = ASC_MAX_PERIOD25;
+ asc->ccf = ASC_CCF(25);
+ asc->timeout_250 = ASC_TIMEOUT_250(25, asc->ccf);
+ asc->tb_ticks = 10;
+ break;
+ case DS_3MIN:
+ case DS_MAXINE:
+ default:
+ asc->min_period = ASC_MIN_PERIOD12;
+ asc->max_period = ASC_MAX_PERIOD12;
+ asc->ccf = ASC_CCF(13);
+ asc->timeout_250 = ASC_TIMEOUT_250(13, asc->ccf);
+ asc->tb_ticks = 20;
+ break;
+ };
+
+ asc->state = ASC_STATE_IDLE;
+ asc->target = -1;
+
+ regs = asc->regs;
+
+ /*
+ * Reset chip, fully. Note that interrupts are already enabled.
+ */
+ s = splbio();
+
+ /* preserve our ID for now */
+ asc->sc_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID;
+ asc->myidmask = ~(1 << asc->sc_id);
+
+ asc_reset(asc, regs);
+
+ /*
+ * Our SCSI id on the bus.
+ * The user can set this via the prom on 3maxen/pmaxen.
+ * If this changes it is easy to fix: make a default that
+ * can be changed as boot arg.
+ */
+#ifdef unneeded
+ regs->asc_cnfg1 = (regs->asc_cnfg1 & ~ASC_CNFG1_MY_BUS_ID) |
+ (scsi_initiator_id[unit] & 0x7);
+ asc->sc_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID;
+#endif
+
+ id = asc->sc_id;
+ splx(s);
+
+ /*
+ * Statically partition the DMA buffer between targets.
+ * This way we will eventually be able to attach/detach
+ * drives on-fly. And 18k/target is plenty for normal use.
+ */
+
+ /*
+ * Give each target its own DMA buffer region.
+ * We may want to try ping ponging buffers later.
+ */
+ for (i = 0; i < ASC_NCMD; i++) {
+ asc->st[i].dmaBufAddr = asc->buff + bufsiz * i;
+ asc->st[i].dmaBufSize = bufsiz;
+ }
+
+ /* Hack for old-sytle SCSI-device probe */
+ (void) pmax_add_scsi(&ascdriver, unit);
+
+ /* tie pseudo-slot to device */
+ BUS_INTR_ESTABLISH(ca, asc_intr, asc);
+ printf(": target %d\n", id);
+
+
+#ifdef USE_NEW_SCSI
+ /*
+ * fill in the prototype scsi_link.
+ */
+ asc->sc_link.adapter_softc = asc;
+ asc->sc_link.adapter_target = asc->sc_id;
+ asc->sc_link.adapter = &asc_switch;
+ asc->sc_link.device = &asc_dev;
+ asc->sc_link.openings = 2;
+
+ /*
+ * Now try to attach all the sub-devices.
+ */
+ config_found(self, &asc->sc_link, ascprint);
+
+#endif /* USE_NEW_SCSI */
+}
+
+/*
+ * Does anyone actually use this, and what for ?
+ */
+int
+ascprint(aux, name)
+ void *aux;
+ char *name;
+{
+ return -1;
+}
+/*
+ * Per Fogelstrom's SCSI Driver breaks down request transfer size.
+ */
+void
+asc_minphys(bp)
+ struct buf *bp;
+{
+ /*XXX*/
+}
+
+/*
+ * Start activity on a SCSI device.
+ * We maintain information on each device separately since devices can
+ * connect/disconnect during an operation.
+ */
+void
+asc_start(scsicmd)
+ register ScsiCmd *scsicmd; /* command to start */
+{
+ register struct pmax_scsi_device *sdp = scsicmd->sd;
+ register asc_softc_t asc = asccd.cd_devs[sdp->sd_ctlr];
+ int s;
+
+ s = splbio();
+ /*
+ * Check if another command is already in progress.
+ * We may have to change this if we allow SCSI devices with
+ * separate LUNs.
+ */
+ if (asc->cmd[sdp->sd_drive]) {
+ printf("%s: device %s busy at start\n", sdp->sd_ctlr,
+ sdp->sd_driver->d_name);
+ (*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,
+ scsicmd->buflen, 0);
+ splx(s);
+ }
+ asc->cmd[sdp->sd_drive] = scsicmd;
+ asc_startcmd(asc, sdp->sd_drive);
+ splx(s);
+}
+
+
+#ifdef USE_NEW_SCSI
+int
+asc_poll(asc, target)
+ struct asc_softc *asc;
+ int target;
+{
+ struct scsi_xfer *scsicmd = asc->cmd[target];
+ int count = scsicmd->timeout * 10;
+
+ while(count) {
+ if(asc->regs->asc_status &ASC_CSR_INT) {
+ asc_intr(asc);
+ }
+ if(scsicmd->flags & ITSDONE)
+ break;
+ DELAY(5);
+ count--;
+ }
+ if(count == 0) {
+ scsicmd->error = XS_TIMEOUT;
+ asc_end(asc, 0, 0, 0);
+ }
+ return COMPLETE;
+}
+#endif /*USE_NEW_SCSI*/
+
+static void
+asc_reset(asc, regs)
+ asc_softc_t asc;
+ asc_regmap_t *regs;
+{
+
+ /*
+ * Reset chip and wait till done
+ */
+ regs->asc_cmd = ASC_CMD_RESET;
+ MachEmptyWriteBuffer(); DELAY(25);
+
+ /* spec says this is needed after reset */
+ regs->asc_cmd = ASC_CMD_NOP;
+ MachEmptyWriteBuffer(); DELAY(25);
+
+ /*
+ * Set up various chip parameters
+ */
+ regs->asc_ccf = asc->ccf;
+ MachEmptyWriteBuffer(); DELAY(25);
+ regs->asc_sel_timo = asc->timeout_250;
+ /* restore our ID */
+ regs->asc_cnfg1 = asc->sc_id | ASC_CNFG1_P_CHECK;
+ /* include ASC_CNFG2_SCSI2 if you want to allow SCSI II commands */
+ regs->asc_cnfg2 = /* ASC_CNFG2_RFB | ASC_CNFG2_SCSI2 | */ ASC_CNFG2_EPL;
+ regs->asc_cnfg3 = 0;
+ /* zero anything else */
+ ASC_TC_PUT(regs, 0);
+ regs->asc_syn_p = asc->min_period;
+ regs->asc_syn_o = 0; /* async for now */
+ MachEmptyWriteBuffer();
+}
+
+/*
+ * Start a SCSI command on a target.
+ */
+static void
+asc_startcmd(asc, target)
+ asc_softc_t asc;
+ int target;
+{
+ register asc_regmap_t *regs;
+ register ScsiCmd *scsicmd;
+ register State *state;
+ int len;
+
+ /*
+ * See if another target is currently selected on this SCSI bus.
+ */
+ if (asc->target >= 0)
+ return;
+
+ regs = asc->regs;
+
+ /*
+ * If a reselection is in progress, it is Ok to ignore it since
+ * the ASC will automatically cancel the command and flush
+ * the FIFO if the ASC is reselected before the command starts.
+ * If we try to use ASC_CMD_DISABLE_SEL, we can hang the system if
+ * a reselect occurs before starting the command.
+ */
+
+ asc->state = ASC_STATE_BUSY;
+ asc->target = target;
+
+ /* cache some pointers */
+ scsicmd = asc->cmd[target];
+ state = &asc->st[target];
+
+#ifdef DEBUG
+ if (asc_debug > 1) {
+ printf("asc_startcmd: %s target %d cmd %x len %d\n",
+ scsicmd->sd->sd_driver->d_name, target,
+ scsicmd->cmd[0], scsicmd->buflen);
+ }
+#endif
+
+ /*
+ * Init the chip and target state.
+ */
+ state->flags = state->flags & DID_SYNC;
+ state->error = 0;
+ state->script = (script_t *)0;
+ state->msg_out = SCSI_NO_OP;
+
+ /*
+ * Copy command data to the DMA buffer.
+ */
+ len = scsicmd->cmdlen;
+ state->dmalen = len;
+ bcopy(scsicmd->cmd, state->dmaBufAddr, len);
+
+ /* check for simple SCSI command with no data transfer */
+ if ((state->buflen = scsicmd->buflen) == 0) {
+ /* check for sync negotiation */
+ if ((scsicmd->flags & SCSICMD_USE_SYNC) &&
+ !(state->flags & DID_SYNC)) {
+ asc->script = &asc_scripts[SCRIPT_TRY_SYNC];
+ state->flags |= TRY_SYNC;
+ } else
+ asc->script = &asc_scripts[SCRIPT_SIMPLE];
+ state->buf = (char *)0;
+ } else if (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) {
+ asc->script = &asc_scripts[SCRIPT_DATA_OUT];
+ state->buf = scsicmd->buf;
+ state->flags |= DMA_OUT;
+ } else {
+ asc->script = &asc_scripts[SCRIPT_DATA_IN];
+ state->buf = scsicmd->buf;
+ state->flags |= DMA_IN;
+ }
+
+#ifdef DEBUG
+ asc_debug_cmd = scsicmd->cmd[0];
+ if (scsicmd->cmd[0] == SCSI_READ_EXT) {
+ asc_debug_bn = (scsicmd->cmd[2] << 24) |
+ (scsicmd->cmd[3] << 16) |
+ (scsicmd->cmd[4] << 8) |
+ scsicmd->cmd[5];
+ asc_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8];
+ }
+ asc_logp->status = PACK(asc->sc_dev.dv_unit, 0, 0, asc_debug_cmd);
+ asc_logp->target = asc->target;
+ asc_logp->state = asc->script - asc_scripts;
+ asc_logp->msg = SCSI_DIS_REC_IDENTIFY;
+ asc_logp->resid = scsicmd->buflen;
+ if (++asc_logp >= &asc_log[NLOG])
+ asc_logp = asc_log;
+#endif
+
+ /* preload the FIFO with the message to be sent */
+ regs->asc_fifo = SCSI_DIS_REC_IDENTIFY;
+ MachEmptyWriteBuffer();
+
+ /* initialize the DMA */
+ (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_WRITE);
+ ASC_TC_PUT(regs, len);
+ readback(regs->asc_cmd);
+
+ regs->asc_dbus_id = target;
+ readback(regs->asc_dbus_id);
+ regs->asc_syn_p = state->sync_period;
+ readback(regs->asc_syn_p);
+ regs->asc_syn_o = state->sync_offset;
+ readback(regs->asc_syn_o);
+
+ if (state->flags & TRY_SYNC)
+ regs->asc_cmd = ASC_CMD_SEL_ATN_STOP;
+ else
+ regs->asc_cmd = ASC_CMD_SEL_ATN | ASC_CMD_DMA;
+ readback(regs->asc_cmd);
+}
+
+/*
+ * Interrupt routine
+ * Take interrupts from the chip
+ *
+ * Implementation:
+ * Move along the current command's script if
+ * all is well, invoke error handler if not.
+ */
+int
+asc_intr(sc)
+ void *sc;
+{
+ register asc_softc_t asc = (asc_softc_t) sc;
+ register asc_regmap_t *regs = asc->regs;
+ register State *state;
+ register script_t *scpt;
+ register int ss, ir, status;
+ register unsigned char cmd_was;
+
+ /* collect ephemeral information */
+ status = regs->asc_status;
+again:
+ ss = regs->asc_ss;
+ cmd_was = regs->asc_cmd;
+
+ /* drop spurious interrupts */
+ if ((status & ASC_CSR_INT) == 0)
+ return;
+
+ ir = regs->asc_intr; /* this resets the previous two: i.e.,*/
+ /* this re-latches CSR (and SSTEP) */
+ scpt = asc->script;
+
+
+#ifdef DEBUG
+ asc_logp->status = PACK(asc->sc_dev.dv_unit, status, ss, ir);
+ asc_logp->target = (asc->state == ASC_STATE_BUSY) ? asc->target : -1;
+ asc_logp->state = scpt - asc_scripts;
+ asc_logp->msg = cmd_was;
+ asc_logp->resid = 0;
+ if (++asc_logp >= &asc_log[NLOG])
+ asc_logp = asc_log;
+ if (asc_debug > 2)
+ printf("asc_intr: status %x ss %x ir %x cond %d:%x\n",
+ status, ss, ir, scpt - asc_scripts, scpt->condition);
+#endif
+
+ /* check the expected state */
+ if (SCRIPT_MATCH(ir, status) == scpt->condition) {
+ /*
+ * Perform the appropriate operation, then proceed.
+ */
+ if ((*scpt->action)(asc, status, ss, ir)) {
+ regs->asc_cmd = scpt->command;
+ readback(regs->asc_cmd);
+ asc->script = scpt->next;
+ }
+ goto done;
+ }
+
+ /*
+ * Check for parity error.
+ * Hardware will automatically set ATN
+ * to request the device for a MSG_OUT phase.
+ */
+ if (status & ASC_CSR_PE) {
+ printf("%s: SCSI device %d: incomming parity error seen\n",
+ asc->sc_dev.dv_xname, asc->target);
+ asc->st[asc->target].flags |= PARITY_ERR;
+ }
+
+ /*
+ * Check for gross error.
+ * Probably a bug in a device driver.
+ */
+ if (status & ASC_CSR_GE) {
+ printf("%s: SCSI device %d: gross error\n",
+ asc->sc_dev.dv_xname, asc->target);
+ goto abort;
+ }
+
+ /* check for message in or out */
+ if ((ir & ~ASC_INT_FC) == ASC_INT_BS) {
+ register int len, fifo;
+
+ state = &asc->st[asc->target];
+ switch (ASC_PHASE(status)) {
+ case ASC_PHASE_DATAI:
+ case ASC_PHASE_DATAO:
+ ASC_TC_GET(regs, len);
+ fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
+ printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n",
+ state->buflen, state->dmalen, len, fifo);
+ goto abort;
+
+ case ASC_PHASE_MSG_IN:
+ break;
+
+ case ASC_PHASE_MSG_OUT:
+ /*
+ * Check for parity error.
+ * Hardware will automatically set ATN
+ * to request the device for a MSG_OUT phase.
+ */
+ if (state->flags & PARITY_ERR) {
+ state->flags &= ~PARITY_ERR;
+ state->msg_out = SCSI_MESSAGE_PARITY_ERROR;
+ /* reset message in counter */
+ state->msglen = 0;
+ } else
+ state->msg_out = SCSI_NO_OP;
+ regs->asc_fifo = state->msg_out;
+ regs->asc_cmd = ASC_CMD_XFER_INFO;
+ readback(regs->asc_cmd);
+ goto done;
+
+ case ASC_PHASE_STATUS:
+ /* probably an error in the SCSI command */
+ asc->script = &asc_scripts[SCRIPT_GET_STATUS];
+ regs->asc_cmd = ASC_CMD_I_COMPLETE;
+ readback(regs->asc_cmd);
+ goto done;
+
+ default:
+ goto abort;
+ }
+
+ if (state->script)
+ goto abort;
+
+ /* check for DMA in progress */
+ ASC_TC_GET(regs, len);
+ fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
+ /* flush any data in the FIFO */
+ if (fifo) {
+ if (state->flags & DMA_OUT) {
+ printf("asc: DMA_OUT, fifo resid %d, len %d, flags 0x%x\n",
+ fifo, len, state->flags);
+ len += fifo;
+ } else if (state->flags & DMA_IN) {
+ u_char *cp;
+
+ printf("asc_intr: IN: dmalen %d len %d fifo %d\n",
+ state->dmalen, len, fifo); /* XXX */
+ len += fifo;
+ cp = state->dmaBufAddr + (state->dmalen - len);
+ while (fifo-- > 0)
+ *cp++ = regs->asc_fifo;
+ } else
+ printf("asc_intr: dmalen %d len %d fifo %d\n",
+ state->dmalen, len, fifo); /* XXX */
+ regs->asc_cmd = ASC_CMD_FLUSH;
+ MachEmptyWriteBuffer();
+ readback(regs->asc_cmd);
+ DELAY(2);
+ }
+ if (len && (state->flags & DMA_IN_PROGRESS)) {
+ /* save number of bytes still to be sent or received */
+ state->dmaresid = len;
+ state->flags &= ~DMA_IN_PROGRESS;
+ ASC_TC_PUT(regs, 0);
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].resid = len;
+ else
+ asc_logp[-1].resid = len;
+#endif
+ /* setup state to resume to */
+ if (state->flags & DMA_IN) {
+ /*
+ * Since the ASC_CNFG3_SRB bit of the
+ * cnfg3 register bit is not set,
+ * we just transferred an extra byte.
+ * Since we can't resume on an odd byte
+ * boundary, we copy the valid data out
+ * and resume DMA at the start address.
+ */
+ if (len & 1) {
+ printf("asc_intr: msg in len %d (fifo %d)\n",
+ len, fifo); /* XXX */
+ len = state->dmalen - len;
+ goto do_in;
+ }
+ state->script =
+ &asc_scripts[SCRIPT_RESUME_DMA_IN];
+ } else if (state->flags & DMA_OUT)
+ state->script =
+ &asc_scripts[SCRIPT_RESUME_DMA_OUT];
+ else
+ state->script = asc->script;
+ } else if (state->flags & DMA_IN) {
+ if (len) {
+#ifdef DEBUG
+ printf("asc_intr: 1: bn %d len %d (fifo %d)\n",
+ asc_debug_bn, len, fifo); /* XXX */
+#endif
+ goto abort;
+ }
+ /* setup state to resume to */
+ if (state->flags & DMA_IN_PROGRESS) {
+ len = state->dmalen;
+ state->flags &= ~DMA_IN_PROGRESS;
+ do_in:
+ (*asc->dma_end)(asc, state, ASCDMA_READ);
+ bcopy(state->dmaBufAddr, state->buf, len);
+ state->buf += len;
+ state->buflen -= len;
+ }
+ if (state->buflen)
+ state->script =
+ &asc_scripts[SCRIPT_RESUME_IN];
+ else
+ state->script =
+ &asc_scripts[SCRIPT_RESUME_NO_DATA];
+ } else if (state->flags & DMA_OUT) {
+ if (len) {
+ printf("asc_intr: 2: len %d (fifo %d)\n", len,
+ fifo); /* XXX */
+ goto abort;
+ }
+ /*
+ * If this is the last chunk, the next expected
+ * state is to get status.
+ */
+ if (state->flags & DMA_IN_PROGRESS) {
+ state->flags &= ~DMA_IN_PROGRESS;
+ (*asc->dma_end)(asc, state, ASCDMA_WRITE);
+ len = state->dmalen;
+ state->buf += len;
+ state->buflen -= len;
+ }
+ if (state->buflen)
+ state->script =
+ &asc_scripts[SCRIPT_RESUME_OUT];
+ else
+ state->script =
+ &asc_scripts[SCRIPT_RESUME_NO_DATA];
+ } else if (asc->script == &asc_scripts[SCRIPT_SIMPLE])
+ state->script = &asc_scripts[SCRIPT_RESUME_NO_DATA];
+ else
+ state->script = asc->script;
+
+ /* setup to receive a message */
+ asc->script = &asc_scripts[SCRIPT_MSG_IN];
+ state->msglen = 0;
+ regs->asc_cmd = ASC_CMD_XFER_INFO;
+ readback(regs->asc_cmd);
+ goto done;
+ }
+
+ /* check for SCSI bus reset */
+ if (ir & ASC_INT_RESET) {
+ register int i;
+
+ printf("%s: SCSI bus reset!!\n", asc->sc_dev.dv_xname);
+ /* need to flush any pending commands */
+ for (i = 0; i < ASC_NCMD; i++) {
+ if (!asc->cmd[i])
+ continue;
+ asc->st[i].error = EIO;
+ asc_end(asc, 0, 0, 0);
+ }
+ /* rearbitrate synchronous offset */
+ for (i = 0; i < ASC_NCMD; i++) {
+ asc->st[i].sync_offset = 0;
+ asc->st[i].flags = 0;
+ }
+ asc->target = -1;
+ return 0 ; /* XXX ??? */
+ }
+
+ /* check for command errors */
+ if (ir & ASC_INT_ILL)
+ goto abort;
+
+ /* check for disconnect */
+ if (ir & ASC_INT_DISC) {
+ state = &asc->st[asc->target];
+ switch (asc->script - asc_scripts) {
+ case SCRIPT_DONE:
+ case SCRIPT_DISCONNECT:
+ /*
+ * Disconnects can happen normally when the
+ * command is complete with the phase being
+ * either ASC_PHASE_DATAO or ASC_PHASE_MSG_IN.
+ * The SCRIPT_MATCH() only checks for one phase
+ * so we can wind up here.
+ * Perform the appropriate operation, then proceed.
+ */
+ if ((*scpt->action)(asc, status, ss, ir)) {
+ regs->asc_cmd = scpt->command;
+ readback(regs->asc_cmd);
+ asc->script = scpt->next;
+ }
+ goto done;
+
+ case SCRIPT_TRY_SYNC:
+ case SCRIPT_SIMPLE:
+ case SCRIPT_DATA_IN:
+ case SCRIPT_DATA_OUT: /* one of the starting scripts */
+ if (ASC_SS(ss) == 0) {
+ /* device did not respond */
+ if (regs->asc_flags & ASC_FLAGS_FIFO_CNT) {
+ regs->asc_cmd = ASC_CMD_FLUSH;
+ readback(regs->asc_cmd);
+ }
+ state->error = ENXIO;
+ asc_end(asc, status, ss, ir);
+ return 0 ; /* XXX ??? */
+ }
+ /* FALLTHROUGH */
+
+ default:
+ printf("%s: SCSI device %d: unexpected disconnect\n",
+ asc->sc_dev.dv_xname, asc->target);
+#ifdef DEBUG
+ asc_DumpLog("asc_disc");
+#endif
+ /*
+ * On rare occasions my RZ24 does a disconnect during
+ * data in phase and the following seems to keep it
+ * happy.
+ * XXX Should a scsi disk ever do this??
+ */
+ asc->script = &asc_scripts[SCRIPT_RESEL];
+ asc->state = ASC_STATE_RESEL;
+ state->flags |= DISCONN;
+ regs->asc_cmd = ASC_CMD_ENABLE_SEL;
+ readback(regs->asc_cmd);
+ return 0 ; /* XXX ??? */
+ }
+ }
+
+ /* check for reselect */
+ if (ir & ASC_INT_RESEL) {
+ unsigned fifo, id, msg;
+
+ fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
+ if (fifo < 2)
+ goto abort;
+ /* read unencoded SCSI ID and convert to binary */
+ msg = regs->asc_fifo & asc->myidmask;
+ for (id = 0; (msg & 1) == 0; id++)
+ msg >>= 1;
+ /* read identify message */
+ msg = regs->asc_fifo;
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].msg = msg;
+ else
+ asc_logp[-1].msg = msg;
+#endif
+ asc->state = ASC_STATE_BUSY;
+ asc->target = id;
+ state = &asc->st[id];
+ asc->script = state->script;
+ state->script = (script_t *)0;
+ if (!(state->flags & DISCONN))
+ goto abort;
+ state->flags &= ~DISCONN;
+ regs->asc_syn_p = state->sync_period;
+ regs->asc_syn_o = state->sync_offset;
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ goto done;
+ }
+
+ /* check if we are being selected as a target */
+ if (ir & (ASC_INT_SEL | ASC_INT_SEL_ATN))
+ goto abort;
+
+ /*
+ * 'ir' must be just ASC_INT_FC.
+ * This is normal if canceling an ASC_ENABLE_SEL.
+ */
+
+done:
+ MachEmptyWriteBuffer();
+ /* watch out for HW race conditions and setup & hold time violations */
+ ir = regs->asc_status;
+ while (ir != (status = regs->asc_status))
+ ir = status;
+ if (status & ASC_CSR_INT)
+ goto again;
+ return 0 ; /* XXX ??? */
+
+abort:
+#ifdef DEBUG
+ asc_DumpLog("asc_intr");
+#endif
+#if 0
+ panic("asc_intr");
+#else
+ boot(4); /* XXX */
+#endif
+}
+
+/*
+ * All the many little things that the interrupt
+ * routine might switch to.
+ */
+
+/* ARGSUSED */
+static int
+script_nop(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_get_status(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register int data;
+
+ /*
+ * Get the last two bytes in the FIFO.
+ */
+ if ((data = regs->asc_flags & ASC_FLAGS_FIFO_CNT) != 2) {
+ printf("asc_get_status: cmdreg %x, fifo cnt %d\n",
+ regs->asc_cmd, data); /* XXX */
+#ifdef DEBUG
+ asc_DumpLog("get_status"); /* XXX */
+#endif
+ if (data < 2) {
+ asc->regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(asc->regs->asc_cmd);
+ return (0);
+ }
+ do {
+ data = regs->asc_fifo;
+ } while ((regs->asc_flags & ASC_FLAGS_FIFO_CNT) > 2);
+ }
+
+ /* save the status byte */
+ asc->st[asc->target].statusByte = data = regs->asc_fifo;
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].msg = data;
+ else
+ asc_logp[-1].msg = data;
+#endif
+
+ /* get the (presumed) command_complete message */
+ if ((data = regs->asc_fifo) == SCSI_COMMAND_COMPLETE)
+ return (1);
+
+#ifdef DEBUG
+ printf("asc_get_status: status %x cmd %x\n",
+ asc->st[asc->target].statusByte, data);
+ asc_DumpLog("asc_get_status");
+#endif
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+asc_end(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register ScsiCmd *scsicmd;
+ register State *state;
+ register int i, target;
+
+ asc->state = ASC_STATE_IDLE;
+ target = asc->target;
+ asc->target = -1;
+ scsicmd = asc->cmd[target];
+ asc->cmd[target] = (ScsiCmd *)0;
+ state = &asc->st[target];
+
+#ifdef DEBUG
+ if (asc_debug > 1) {
+ printf("asc_end: %s target %d cmd %x err %d resid %d\n",
+ scsicmd->sd->sd_driver->d_name, target,
+ scsicmd->cmd[0], state->error, state->buflen);
+ }
+#endif
+#ifdef DIAGNOSTIC
+ if (target < 0 || !scsicmd)
+ panic("asc_end");
+#endif
+
+ /* look for disconnected devices */
+ for (i = 0; i < ASC_NCMD; i++) {
+ if (!asc->cmd[i] || !(asc->st[i].flags & DISCONN))
+ continue;
+ asc->regs->asc_cmd = ASC_CMD_ENABLE_SEL;
+ readback(asc->regs->asc_cmd);
+ asc->state = ASC_STATE_RESEL;
+ asc->script = &asc_scripts[SCRIPT_RESEL];
+ break;
+ }
+
+#ifdef USE_NEW_SCSI
+ if(scsicmd->error == XS_NOERROR && !(state->flags & CHECK_SENSE)) {
+ if((state->statusByte & ST_MASK) == SCSI_CHECK) {
+ struct scsi_sense *ss = (void *)&state->cmd;
+ /* Save return values */
+ scsicmd->resid = state->buflen;
+ scsicmd->status = state->statusByte;
+ /* Set up sense request command */
+ bzero(ss, sizeof(*ss));
+ ss->opcode = REQUEST_SENSE;
+ ss->byte2 = sc_link->lun << 5;
+ ss->length = sizeof(struct scsi_sense_data);
+ state->cmdlen = sizeof(*ss);
+ state->buf = (vm_offset_t)&scsicmd->sense;
+ state->buflen = sizeof(struct scsi_sense_data);
+ state->flags |= CHECK_SENSE;
+ MachFlushDCache(state->buf, state->buflen);
+ asc->cmd[target] = scsicmd;
+ asc_startcmd(asc, target);
+ return(0);
+ }
+ }
+#endif /*USE_NEW_SCSI*/
+
+ /*
+ * Look for another device that is ready.
+ * May want to keep last one started and increment for fairness
+ * rather than always starting at zero.
+ */
+ for (i = 0; i < ASC_NCMD; i++) {
+ /* don't restart a disconnected command */
+ if (!asc->cmd[i] || (asc->st[i].flags & DISCONN))
+ continue;
+ asc_startcmd(asc, i);
+ break;
+ }
+
+ /* signal device driver that the command is done */
+ (*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, state->error,
+ state->buflen, state->statusByte);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+asc_dma_in(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len;
+
+ /* check for previous chunk in buffer */
+ if (state->flags & DMA_IN_PROGRESS) {
+ /*
+ * Only count bytes that have been copied to memory.
+ * There may be some bytes in the FIFO if synchonous transfers
+ * are in progress.
+ */
+ (*asc->dma_end)(asc, state, ASCDMA_READ);
+ ASC_TC_GET(regs, len);
+ len = state->dmalen - len;
+ bcopy(state->dmaBufAddr, state->buf, len);
+ state->buf += len;
+ state->buflen -= len;
+ }
+
+ /* setup to start reading the next chunk */
+ len = state->buflen;
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].resid = len;
+ else
+ asc_logp[-1].resid = len;
+#endif
+ if (len > state->dmaBufSize)
+ len = state->dmaBufSize;
+ state->dmalen = len;
+ (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_READ);
+ ASC_TC_PUT(regs, len);
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_dma_in: buflen %d, len %d\n", state->buflen, len);
+#endif
+
+ /* check for next chunk */
+ state->flags |= DMA_IN_PROGRESS;
+ if (len != state->buflen) {
+ regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
+ readback(regs->asc_cmd);
+ asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
+ return (0);
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_last_dma_in(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len, fifo;
+
+ /* copy data from buffer to main memory */
+ (*asc->dma_end)(asc, state, ASCDMA_READ);
+ ASC_TC_GET(regs, len);
+ fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_last_dma_in: buflen %d dmalen %d tc %d fifo %d\n",
+ state->buflen, state->dmalen, len, fifo);
+#endif
+ if (fifo) {
+ /* device must be trying to send more than we expect */
+ regs->asc_cmd = ASC_CMD_FLUSH;
+ readback(regs->asc_cmd);
+ }
+ state->flags &= ~DMA_IN_PROGRESS;
+ len = state->dmalen - len;
+ state->buflen -= len;
+ bcopy(state->dmaBufAddr, state->buf, len);
+
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_resume_in(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len;
+
+ /* setup to start reading the next chunk */
+ len = state->buflen;
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].resid = len;
+ else
+ asc_logp[-1].resid = len;
+#endif
+ if (len > state->dmaBufSize)
+ len = state->dmaBufSize;
+ state->dmalen = len;
+ (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_READ);
+ ASC_TC_PUT(regs, len);
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_resume_in: buflen %d, len %d\n", state->buflen,
+ len);
+#endif
+
+ /* check for next chunk */
+ state->flags |= DMA_IN_PROGRESS;
+ if (len != state->buflen) {
+ regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
+ readback(regs->asc_cmd);
+ asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
+ return (0);
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_resume_dma_in(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len, off;
+
+ /* setup to finish reading the current chunk */
+ len = state->dmaresid;
+ off = state->dmalen - len;
+ if ((off & 1) && state->sync_offset) {
+ printf("asc_resume_dma_in: odd xfer dmalen %d len %d off %d\n",
+ state->dmalen, len, off); /* XXX */
+ regs->asc_res_fifo = state->dmaBufAddr[off];
+ }
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].resid = len;
+ else
+ asc_logp[-1].resid = len;
+#endif
+ (*asc->dma_start)(asc, state, state->dmaBufAddr + off, ASCDMA_READ);
+ ASC_TC_PUT(regs, len);
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_resume_dma_in: buflen %d dmalen %d len %d off %d\n",
+ state->dmalen, state->buflen, len, off);
+#endif
+
+ /* check for next chunk */
+ state->flags |= DMA_IN_PROGRESS;
+ if (state->dmalen != state->buflen) {
+ regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
+ readback(regs->asc_cmd);
+ asc->script = &asc_scripts[SCRIPT_CONTINUE_IN];
+ return (0);
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_dma_out(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len, fifo;
+
+ if (state->flags & DMA_IN_PROGRESS) {
+ /* check to be sure previous chunk was finished */
+ ASC_TC_GET(regs, len);
+ fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
+ if (len || fifo)
+ printf("asc_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
+ state->buflen, state->dmalen, len, fifo); /* XXX */
+ len += fifo;
+ len = state->dmalen - len;
+ state->buf += len;
+ state->buflen -= len;
+ }
+
+ /* setup for this chunk */
+ len = state->buflen;
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].resid = len;
+ else
+ asc_logp[-1].resid = len;
+#endif
+ if (len > state->dmaBufSize)
+ len = state->dmaBufSize;
+ state->dmalen = len;
+ bcopy(state->buf, state->dmaBufAddr, len);
+ (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_WRITE);
+ ASC_TC_PUT(regs, len);
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_dma_out: buflen %d, len %d\n", state->buflen, len);
+#endif
+
+ /* check for next chunk */
+ state->flags |= DMA_IN_PROGRESS;
+ if (len != state->buflen) {
+ regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
+ readback(regs->asc_cmd);
+ asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
+ return (0);
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_last_dma_out(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len, fifo;
+
+ ASC_TC_GET(regs, len);
+ fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT;
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
+ state->buflen, state->dmalen, len, fifo);
+#endif
+ if (fifo) {
+ len += fifo;
+ regs->asc_cmd = ASC_CMD_FLUSH;
+ readback(regs->asc_cmd);
+ printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
+ state->buflen, state->dmalen, len, fifo);
+ }
+ state->flags &= ~DMA_IN_PROGRESS;
+ len = state->dmalen - len;
+ state->buflen -= len;
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_resume_out(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len;
+
+ /* setup for this chunk */
+ len = state->buflen;
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].resid = len;
+ else
+ asc_logp[-1].resid = len;
+#endif
+ if (len > state->dmaBufSize)
+ len = state->dmaBufSize;
+ state->dmalen = len;
+ bcopy(state->buf, state->dmaBufAddr, len);
+ (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_WRITE);
+ ASC_TC_PUT(regs, len);
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_resume_out: buflen %d, len %d\n", state->buflen,
+ len);
+#endif
+
+ /* check for next chunk */
+ state->flags |= DMA_IN_PROGRESS;
+ if (len != state->buflen) {
+ regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
+ readback(regs->asc_cmd);
+ asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
+ return (0);
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_resume_dma_out(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int len, off;
+
+ /* setup to finish writing this chunk */
+ len = state->dmaresid;
+ off = state->dmalen - len;
+ if (off & 1) {
+ printf("asc_resume_dma_out: odd xfer dmalen %d len %d off %d\n",
+ state->dmalen, len, off); /* XXX */
+ regs->asc_fifo = state->dmaBufAddr[off];
+ off++;
+ len--;
+ }
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].resid = len;
+ else
+ asc_logp[-1].resid = len;
+#endif
+ (*asc->dma_start)(asc, state, state->dmaBufAddr + off, ASCDMA_WRITE);
+ ASC_TC_PUT(regs, len);
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_resume_dma_out: buflen %d dmalen %d len %d off %d\n",
+ state->dmalen, state->buflen, len, off);
+#endif
+
+ /* check for next chunk */
+ state->flags |= DMA_IN_PROGRESS;
+ if (state->dmalen != state->buflen) {
+ regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA;
+ readback(regs->asc_cmd);
+ asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT];
+ return (0);
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_sendsync(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+
+ /* send the extended synchronous negotiation message */
+ regs->asc_fifo = SCSI_EXTENDED_MSG;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = 3;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = SCSI_SYNCHRONOUS_XFER;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = SCSI_MIN_PERIOD;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = ASC_MAX_OFFSET;
+ /* state to resume after we see the sync reply message */
+ state->script = asc->script + 2;
+ state->msglen = 0;
+ return (1);
+}
+
+/* ARGSUSED */
+static int
+asc_replysync(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_replysync: %x %x\n",
+ asc_to_scsi_period[state->sync_period] * asc->tb_ticks,
+ state->sync_offset);
+#endif
+ /* send synchronous transfer in response to a request */
+ regs->asc_fifo = SCSI_EXTENDED_MSG;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = 3;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = SCSI_SYNCHRONOUS_XFER;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = asc_to_scsi_period[state->sync_period] * asc->tb_ticks;
+ MachEmptyWriteBuffer();
+ regs->asc_fifo = state->sync_offset;
+ regs->asc_cmd = ASC_CMD_XFER_INFO;
+ readback(regs->asc_cmd);
+
+ /* return to the appropriate script */
+ if (!state->script) {
+#ifdef DEBUG
+ asc_DumpLog("asc_replsync");
+#endif
+ panic("asc_replysync");
+ }
+ asc->script = state->script;
+ state->script = (script_t *)0;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+asc_msg_in(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register asc_regmap_t *regs = asc->regs;
+ register State *state = &asc->st[asc->target];
+ register int msg;
+ int i;
+
+ /* read one message byte */
+ msg = regs->asc_fifo;
+#ifdef DEBUG
+ if (asc_logp == asc_log)
+ asc_log[NLOG - 1].msg = msg;
+ else
+ asc_logp[-1].msg = msg;
+#endif
+
+ /* check for multi-byte message */
+ if (state->msglen != 0) {
+ /* first byte is the message length */
+ if (state->msglen < 0) {
+ state->msglen = msg;
+ return (1);
+ }
+ if (state->msgcnt >= state->msglen)
+ goto abort;
+ state->msg_in[state->msgcnt++] = msg;
+
+ /* did we just read the last byte of the message? */
+ if (state->msgcnt != state->msglen)
+ return (1);
+
+ /* process an extended message */
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_msg_in: msg %x %x %x\n",
+ state->msg_in[0],
+ state->msg_in[1],
+ state->msg_in[2]);
+#endif
+ switch (state->msg_in[0]) {
+ case SCSI_SYNCHRONOUS_XFER:
+ state->flags |= DID_SYNC;
+ state->sync_offset = state->msg_in[2];
+
+ /* convert SCSI period to ASC period */
+ i = state->msg_in[1] / asc->tb_ticks;
+ if (i < asc->min_period)
+ i = asc->min_period;
+ else if (i >= asc->max_period) {
+ /* can't do sync transfer, period too long */
+ printf("%s: SCSI device %d: sync xfer period too long (%d)\n",
+ asc->sc_dev.dv_xname, asc->target, i);
+ i = asc->max_period;
+ state->sync_offset = 0;
+ }
+ if ((i * asc->tb_ticks) != state->msg_in[1])
+ i++;
+ state->sync_period = i & 0x1F;
+
+ /*
+ * If this is a request, check minimums and
+ * send back an acknowledge.
+ */
+ if (!(state->flags & TRY_SYNC)) {
+ regs->asc_cmd = ASC_CMD_SET_ATN;
+ readback(regs->asc_cmd);
+
+ if (state->sync_period < asc->min_period)
+ state->sync_period =
+ asc->min_period;
+ if (state->sync_offset > ASC_MAX_OFFSET)
+ state->sync_offset =
+ ASC_MAX_OFFSET;
+ asc->script = &asc_scripts[SCRIPT_REPLY_SYNC];
+ regs->asc_syn_p = state->sync_period;
+ readback(regs->asc_syn_p);
+ regs->asc_syn_o = state->sync_offset;
+ readback(regs->asc_syn_o);
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ return (0);
+ }
+
+ regs->asc_syn_p = state->sync_period;
+ readback(regs->asc_syn_p);
+ regs->asc_syn_o = state->sync_offset;
+ readback(regs->asc_syn_o);
+ goto done;
+
+ default:
+ printf("%s: SCSI device %d: rejecting extended message 0x%x\n",
+ asc->sc_dev.dv_xname, asc->target,
+ state->msg_in[0]);
+ goto reject;
+ }
+ }
+
+ /* process first byte of a message */
+#ifdef DEBUG
+ if (asc_debug > 2)
+ printf("asc_msg_in: msg %x\n", msg);
+#endif
+ switch (msg) {
+#if 0
+ case SCSI_MESSAGE_REJECT:
+ printf(" did not like SYNCH xfer "); /* XXX */
+ state->flags |= DID_SYNC;
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ status = asc_wait(regs, ASC_CSR_INT);
+ ir = regs->asc_intr;
+ /* some just break out here, some dont */
+ if (ASC_PHASE(status) == ASC_PHASE_MSG_OUT) {
+ regs->asc_fifo = SCSI_ABORT;
+ regs->asc_cmd = ASC_CMD_XFER_INFO;
+ readback(regs->asc_cmd);
+ status = asc_wait(regs, ASC_CSR_INT);
+ ir = regs->asc_intr;
+ }
+ if (ir & ASC_INT_DISC) {
+ asc_end(asc, status, 0, ir);
+ return (0);
+ }
+ goto status;
+#endif /*0*/
+
+ case SCSI_EXTENDED_MSG: /* read an extended message */
+ /* setup to read message length next */
+ state->msglen = -1;
+ state->msgcnt = 0;
+ return (1);
+
+ case SCSI_NO_OP:
+ break;
+
+ case SCSI_SAVE_DATA_POINTER:
+ /* expect another message */
+ return (1);
+
+ case SCSI_RESTORE_POINTERS:
+ /*
+ * Need to do the following if resuming synchonous data in
+ * on an odd byte boundary.
+ regs->asc_cnfg2 |= ASC_CNFG2_RFB;
+ */
+ break;
+
+ case SCSI_DISCONNECT:
+ if (state->flags & DISCONN)
+ goto abort;
+ state->flags |= DISCONN;
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ asc->script = &asc_scripts[SCRIPT_DISCONNECT];
+ return (0);
+
+ default:
+ printf("%s: SCSI device %d: rejecting message 0x%x\n",
+ asc->sc_dev.dv_xname, asc->target, msg);
+ reject:
+ /* request a message out before acknowledging this message */
+ state->msg_out = SCSI_MESSAGE_REJECT;
+ regs->asc_cmd = ASC_CMD_SET_ATN;
+ readback(regs->asc_cmd);
+ }
+
+done:
+ /* return to original script */
+ regs->asc_cmd = ASC_CMD_MSG_ACPT;
+ readback(regs->asc_cmd);
+ if (!state->script) {
+ abort:
+#ifdef DEBUG
+ asc_DumpLog("asc_msg_in");
+#endif
+ panic("asc_msg_in");
+ }
+ asc->script = state->script;
+ state->script = (script_t *)0;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+asc_disconnect(asc, status, ss, ir)
+ register asc_softc_t asc;
+ register int status, ss, ir;
+{
+ register State *state = &asc->st[asc->target];
+
+#ifdef DIAGNOSTIC
+ if (!(state->flags & DISCONN)) {
+ printf("asc_disconnect: device %d: DISCONN not set!\n",
+ asc->target);
+ }
+#endif /*DIAGNOSTIC*/
+ asc->target = -1;
+ asc->state = ASC_STATE_RESEL;
+ return (1);
+}
+
+/*
+ * DMA handling routines. For a turbochannel device, just set the dmar.
+ * For the I/O ASIC, handle the actual DMA interface.
+ */
+static void
+tb_dma_start(asc, state, cp, flag)
+ asc_softc_t asc;
+ State *state;
+ caddr_t cp;
+ int flag;
+{
+
+ if (flag == ASCDMA_WRITE)
+ *asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(cp);
+ else
+ *asc->dmar = ASC_DMA_ADDR(cp);
+}
+
+static void
+tb_dma_end(asc, state, flag)
+ asc_softc_t asc;
+ State *state;
+ int flag;
+{
+
+}
+
+static void
+asic_dma_start(asc, state, cp, flag)
+ asc_softc_t asc;
+ State *state;
+ caddr_t cp;
+ int flag;
+{
+ register volatile u_int *ssr = (volatile u_int *)
+ ASIC_REG_CSR(asic_base);
+ u_int phys, nphys;
+
+ /* stop DMA engine first */
+ *ssr &= ~ASIC_CSR_DMAEN_SCSI;
+ *((volatile int *)ASIC_REG_SCSI_SCR(asic_base)) = 0;
+
+ phys = MACH_CACHED_TO_PHYS(cp);
+ cp = (caddr_t)pmax_trunc_page(cp + NBPG);
+ nphys = MACH_CACHED_TO_PHYS(cp);
+
+ asc->dma_next = cp;
+ asc->dma_xfer = state->dmalen - (nphys - phys);
+
+ *(volatile int *)ASIC_REG_SCSI_DMAPTR(asic_base) =
+ ASIC_DMA_ADDR(phys);
+ *(volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base) =
+ ASIC_DMA_ADDR(nphys);
+ if (flag == ASCDMA_READ)
+ *ssr |= ASIC_CSR_SCSI_DIR | ASIC_CSR_DMAEN_SCSI;
+ else
+ *ssr = (*ssr & ~ASIC_CSR_SCSI_DIR) | ASIC_CSR_DMAEN_SCSI;
+ MachEmptyWriteBuffer();
+}
+
+static void
+asic_dma_end(asc, state, flag)
+ asc_softc_t asc;
+ State *state;
+ int flag;
+{
+ register volatile u_int *ssr = (volatile u_int *)
+ ASIC_REG_CSR(asic_base);
+ register volatile u_int *dmap = (volatile u_int *)
+ ASIC_REG_SCSI_DMAPTR(asic_base);
+ register u_short *to;
+ register int w;
+ int nb;
+
+ *ssr &= ~ASIC_CSR_DMAEN_SCSI;
+ to = (u_short *)MACH_PHYS_TO_CACHED(*dmap >> 3);
+ *dmap = -1;
+ *((volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base)) = -1;
+ MachEmptyWriteBuffer();
+
+ if (flag == ASCDMA_READ) {
+ MachFlushDCache(MACH_PHYS_TO_CACHED(
+ MACH_UNCACHED_TO_PHYS(state->dmaBufAddr)), state->dmalen);
+ if (nb = *((int *)ASIC_REG_SCSI_SCR(asic_base))) {
+ /* pick up last upto6 bytes, sigh. */
+
+ /* Last byte really xferred is.. */
+ w = *(int *)ASIC_REG_SCSI_SDR0(asic_base);
+ *to++ = w;
+ if (--nb > 0) {
+ w >>= 16;
+ *to++ = w;
+ }
+ if (--nb > 0) {
+ w = *(int *)ASIC_REG_SCSI_SDR1(asic_base);
+ *to++ = w;
+ }
+ }
+ }
+}
+
+#ifdef notdef
+/*
+ * Called by asic_intr() for scsi dma pointer update interrupts.
+ */
+void
+asc_dma_intr()
+{
+ asc_softc_t asc = &asccd.cd_devs[0]; /*XXX*/
+ u_int next_phys;
+
+ asc->dma_xfer -= NBPG;
+ if (asc->dma_xfer <= -NBPG) {
+ volatile u_int *ssr = (volatile u_int *)
+ ASIC_REG_CSR(asic_base);
+ *ssr &= ~ASIC_CSR_DMAEN_SCSI;
+ } else {
+ asc->dma_next += NBPG;
+ next_phys = MACH_CACHED_TO_PHYS(asc->dma_next);
+ }
+ *(volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base) =
+ ASIC_DMA_ADDR(next_phys);
+ MachEmptyWriteBuffer();
+}
+#endif /*notdef*/
+
+#ifdef DEBUG
+asc_DumpLog(str)
+ char *str;
+{
+ register struct asc_log *lp;
+ register u_int status;
+
+ printf("asc: %s: cmd %x bn %d cnt %d\n", str, asc_debug_cmd,
+ asc_debug_bn, asc_debug_sz);
+ lp = asc_logp;
+ do {
+ status = lp->status;
+ printf("asc%d tgt %d status %x ss %x ir %x cond %d:%x msg %x resid %d\n",
+ status >> 24,
+ lp->target,
+ (status >> 16) & 0xFF,
+ (status >> 8) & 0xFF,
+ status & 0XFF,
+ lp->state,
+ asc_scripts[lp->state].condition,
+ lp->msg, lp->resid);
+ if (++lp >= &asc_log[NLOG])
+ lp = asc_log;
+ } while (lp != asc_logp);
+}
+#endif /*DEBUG*/
+
+#endif /* NASC > 0 */