summaryrefslogtreecommitdiff
path: root/scsi/adapters/scsi_89352_hdw.c
diff options
context:
space:
mode:
Diffstat (limited to 'scsi/adapters/scsi_89352_hdw.c')
-rw-r--r--scsi/adapters/scsi_89352_hdw.c2192
1 files changed, 0 insertions, 2192 deletions
diff --git a/scsi/adapters/scsi_89352_hdw.c b/scsi/adapters/scsi_89352_hdw.c
deleted file mode 100644
index 5672cb6..0000000
--- a/scsi/adapters/scsi_89352_hdw.c
+++ /dev/null
@@ -1,2192 +0,0 @@
-/*
- * Mach Operating System
- * Copyright (c) 1992,1991 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_89352_hdw.c
- * Author: Daniel Stodolsky, Carnegie Mellon University
- * Date: 06/91
- *
- * Bottom layer of the SCSI driver: chip-dependent functions
- *
- * This file contains the code that is specific to the Fujitsu MB89352
- * 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
- *
- */
-
-/*
- *
- *
- * Known Headaches/Features with this chip.
- *
- * (1) After the interrupt raised by select, the phase sense (psns)
- * and SPC status (ssts) registers do not display the correct values
- * until the REQ line (via psns) is high. (danner@cs.cmu.edu 6/11/91)
- *
- * (2) After a data in phase, the command complete interrupt may be raised
- * before the psns, ssts, and transfer counter registers settle. The reset
- * acknowledge or request command should not be issued until they settle.
- * (danner@cs.cmu.edu 6/14/91)
- *
- * (3) In general, an interrupt can be raised before the psns and ssts have
- * meaningful values. One should wait for the psns to show the REQ bit (0x80)
- * set before expecting meaningful values, with the exception of (2) above.
- * Currently this is handled by spc_err_generic ("Late REQ"). (This problem
- * is really a refinement of (1)). (danner@cs.cmu.edu 6/14/91)
- *
- * (4) When issuing a multibyte command after a select with attention,
- * The chip will automatically drop ATN before sending the last byte of the
- * message, in accordance with the ANSI SCSI standard. This requires, of course,
- * the transfer counter be an accurate representation of the amount of data to be
- * transfered. (danner@cs.cmu.edu 6/14/91)
- *
- */
-
-#if 0
-
-#include <platforms.h>
-
-#include <scsi.h>
-
-#if NSCSI > 0
-
-#include <mach/std_types.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_89352.h>
-
-#include <machine/db_machdep.h> /*4proto*/
-#include <ddb/db_sym.h> /*4proto*/
-
-#ifdef LUNA88K
-#include <luna88k/board.h>
-#define SPC_DEFAULT_ADDRESS (caddr_t) SCSI_ADDR
-#endif
-
-#ifndef SPC_DEFAULT_ADDRESS /* cross compile check */
-#define SPC_DEFAULT_ADDRESS (caddr_t) 0
-#endif
-
-
-/* external/forward declarations */
-int spc_probe(), spc_slave(), spc_attach(), scsi_go();
-void spc_reset(), spc_attempt_selection(), spc_target_intr(), spc_bus_reset();
-/*
- * Statically allocated command & temp buffers
- * This way we can attach/detach drives on-fly
- */
-#define PER_TGT_BUFF_DATA 256
-
-static char spc_buffer[NSCSI * 8 * PER_TGT_BUFF_DATA];
-
-/*
- * Macros to make certain things a little more readable
- */
-
-/*
- wait for the desired phase to appear, but make sure the REQ bit set in the psns
- (otherwise the values tend to float/be garbage.
-*/
-
-#define SPC_WAIT_PHASE(p) while(((regs->spc_psns & (SPC_BUS_REQ|SCSI_PHASE_MASK))) \
- != (SPC_BUS_REQ|(p)))
-
-/*
- wait until a phase different than p appears in the psns. Since it is only valid
- when the REQ bit is set, don't test unless REQ bit is set. So spin until
- REQ is high or the phase is not p.
-*/
-
-#define SPC_WAIT_PHASE_VANISH(p) while(1) { int _psns_ = regs->spc_psns; \
- if ((_psns_ & SPC_BUS_REQ) && (_psns_ & SCSI_PHASE_MASK)!=p) break; }
-
-
-
-/* ?? */
-/* #define SPC_ACK(ptr,phase) (ptr)->spc_pctl = (phase) */
-
-/*
- * A script has a two parts: a pre-condition and an action.
- * The first triggers error handling if not satisfied and in
- * our case it is formed by the current bus phase and connected
- * condition as per bus status bits. The action part is just a
- * function pointer, invoked in a standard way. The script
- * pointer is advanced only if the action routine returns TRUE.
- * See spc_intr() for how and where this is all done.
- */
-
-typedef struct script {
- char condition; /* expected state at interrupt */
- int (*action)(); /* action routine */
-} *script_t;
-
-#define SCRIPT_MATCH(psns) (SPC_CUR_PHASE((psns))|((psns) & SPC_BUS_BSY))
-
-/* ?? */
-#define SPC_PHASE_DISC 0x0 /* sort of .. */
-
-/* The active script is in the state expected right after the issue of a select */
-
-#define SCRIPT_SELECT(scp) (scp->action == spc_issue_command || \
- scp->action == spc_issue_ident_and_command)
-
-/* forward decls of script actions */
-boolean_t
- spc_dosynch(), /* negotiate synch xfer */
- spc_xfer_in(), /* get data from target via dma */
- spc_xfer_out(), /* send data to target via dma */
- spc_get_status(), /* get status from target */
- spc_end_transaction(), /* all come to an end */
- spc_msg_in(), /* get disconnect message(s) */
- spc_issue_command(), /* spit on the bus */
- spc_issue_ident_and_command(), /* spit on the bus (with ATN) */
- spc_disconnected(); /* current target disconnected */
-/* forward decls of error handlers */
-boolean_t
- spc_err_generic(), /* generic error handler */
- spc_err_disconn(); /* when a target disconnects */
-void gimmeabreak(); /* drop into the debugger */
-
-void spc_reset_scsibus();
-boolean_t spc_probe_target();
-
-scsi_ret_t spc_select_target();
-
-/*
- * State descriptor for this layer. There is one such structure
- * per (enabled) 89352 chip
- */
-struct spc_softc {
- watchdog_t wd;
- spc_regmap_t *regs; /* 5380 registers */
- char *buff; /* scratch buffer memory */
- char *data_ptr; /* orig/dest memory */
- script_t script;
- int (*error_handler)();
- int in_count; /* amnt we expect to receive */
- int out_count; /* amnt we are going to ship */
-
- volatile char state;
-#define SPC_STATE_BUSY 0x01 /* selecting or currently connected */
-#define SPC_STATE_TARGET 0x04 /* currently selected as target */
-#define SPC_STATE_COLLISION 0x08 /* lost selection attempt */
-#define SPC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */
-
- unsigned char ntargets; /* how many alive on this scsibus */
- unsigned char done;
- unsigned char xxxx;
-
- scsi_softc_t *sc;
- target_info_t *active_target;
-
- target_info_t *next_target; /* trying to seize bus */
- queue_head_t waiting_targets;/* other targets competing for bus */
- decl_simple_lock_data(,chiplock) /* Interlock */
-} spc_softc_data[NSCSI];
-
-typedef struct spc_softc *spc_softc_t;
-
-spc_softc_t spc_softc[NSCSI];
-
-/*
- * Definition of the controller for the auto-configuration program.
- */
-
-int spc_probe(), scsi_slave(), spc_go();
-void spc_intr();
-void scsi_attach();
-
-vm_offset_t spc_std[NSCSI] = { SPC_DEFAULT_ADDRESS };
-
-struct bus_device *spc_dinfo[NSCSI*8];
-struct bus_ctlr *spc_minfo[NSCSI];
-struct bus_driver spc_driver =
- { spc_probe, scsi_slave, scsi_attach, spc_go, spc_std, "rz", spc_dinfo,
- "spc", spc_minfo, BUS_INTR_B4_PROBE};
-
-/*
- * Scripts
- */
-
-struct script
-spc_script_data_in[] = {
- { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command},
- { SCSI_PHASE_DATAI|SPC_BUS_BSY, spc_xfer_in},
- { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
- { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
-},
-
-spc_script_late_data_in[] = {
- { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command},
- { SCSI_PHASE_DATAI|SPC_BUS_BSY, spc_xfer_in},
- { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
- { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
-},
-
-spc_script_data_out[] = {
- { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command},
- { SCSI_PHASE_DATAO|SPC_BUS_BSY, spc_xfer_out},
- { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
- { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
-},
-
-
-spc_script_late_data_out[] = {
- { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command},
- { SCSI_PHASE_DATAO|SPC_BUS_BSY, spc_xfer_out},
- { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
- { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
-},
-
-
-spc_script_cmd[] = {
- { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command},
- { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
- { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
-},
-
-spc_script_late_cmd[] = {
- { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command},
- { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status},
- { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction}
-},
-
-/* Synchronous transfer neg(oti)ation */
-
-spc_script_try_synch[] = {
- { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_dosynch}
-},
-
-/* Disconnect sequence */
-
-spc_script_disconnect[] = {
- { SPC_PHASE_DISC, spc_disconnected}
-};
-
-
-
-#define u_min(a,b) (((a) < (b)) ? (a) : (b))
-
-
-#define DEBUG
-#ifdef DEBUG
-
-int spc_state(base)
- vm_offset_t base;
-{
- register spc_regmap_t *regs;
-
- if (base == 0)
- base = (vm_offset_t) SPC_DEFAULT_ADDRESS;
-
- regs = (spc_regmap_t*) (base);
-
- db_printf("spc_bdid (bus device #): %x\n",regs->spc_bdid);
- db_printf("spc_sctl (spc internal control): %x\n",regs->spc_sctl);
- db_printf("spc_scmd (scp command): %x\n",regs->spc_scmd);
- db_printf("spc_ints (spc interrupt): %x\n",regs->spc_ints);
- db_printf("spc_psns (scsi bus phase): %x\n",regs->spc_psns);
- db_printf("spc_ssts (spc internal status): %x\n",regs->spc_ssts);
- db_printf("spc_serr (spc internal err stat): %x\n",regs->spc_serr);
- db_printf("spc_pctl (scsi transfer phase): %x\n",regs->spc_pctl);
- db_printf("spc_mbc (spc transfer data ct): %x\n",regs->spc_mbc);
-/* db_printf("spc_dreg (spc transfer data r/w): %x\n",regs->spc_dreg);*/
- db_printf("spc_temp (scsi data bus control): %x\n",regs->spc_temp);
- db_printf("spc_tch (transfer byte ct (MSB): %x\n",regs->spc_tch);
- db_printf("spc_tcm (transfer byte ct (2nd): %x\n",regs->spc_tcm);
- db_printf("spc_tcl (transfer byte ct (LSB): %x\n",regs->spc_tcl);
-
- return 0;
-}
-
-int spc_target_state(tgt)
- target_info_t *tgt;
-{
- if (tgt == 0)
- tgt = spc_softc[0]->active_target;
- if (tgt == 0)
- return 0;
- db_printf("fl %x dma %x+%x cmd %x id %x per %x off %x ior %x ret %x\n",
- tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset,
- tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset,
- tgt->ior, tgt->done);
- if (tgt->flags & TGT_DISCONNECTED){
- script_t spt;
-
- spt = tgt->transient_state.script;
- db_printf("disconnected at ");
- db_printsym((db_expr_t)spt,1);
- db_printf(": %x ", spt->condition);
- db_printsym((db_expr_t)spt->action,1);
- db_printf(", ");
- db_printsym((db_expr_t)tgt->transient_state.handler, 1);
- db_printf("\n");
- }
-
- return 0;
-}
-
-void spc_all_targets(unit)
-int unit;
-{
- int i;
- target_info_t *tgt;
- for (i = 0; i < 8; i++) {
- tgt = spc_softc[unit]->sc->target[i];
- if (tgt)
- spc_target_state(tgt);
- }
-}
-
-int spc_script_state(unit)
-int unit;
-{
- script_t spt = spc_softc[unit]->script;
-
- if (spt == 0) return 0;
- db_printsym((db_expr_t)spt,1);
- db_printf(": %x ", spt->condition);
- db_printsym((db_expr_t)spt->action,1);
- db_printf(", ");
- db_printsym((db_expr_t)spc_softc[unit]->error_handler, 1);
- return 0;
-}
-
-#define PRINT(x) if (scsi_debug) printf x
-
-#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
-int spc_logpt;
-int spc_log[LOGSIZE];
-
-#define MAXLOG_VALUE 0x30
-struct {
- char *name;
- unsigned int count;
-} logtbl[MAXLOG_VALUE];
-
-static void LOG(e,f)
- int e;
- char *f;
-{
- spc_log[spc_logpt++] = (e);
- if (spc_logpt == LOGSIZE) spc_logpt = 0;
- if ((e) < MAXLOG_VALUE) {
- logtbl[(e)].name = (f);
- logtbl[(e)].count++;
- }
-}
-
-int spc_print_log(skip)
- int skip;
-{
- register int i, j;
- register unsigned int c;
-
- for (i = 0, j = spc_logpt; i < LOGSIZE; i++) {
- c = spc_log[j];
- if (++j == LOGSIZE) j = 0;
- if (skip-- > 0)
- continue;
- if (c < MAXLOG_VALUE)
- db_printf(" %s", logtbl[c].name);
- else
- db_printf("-0x%x", c - 0x80);
- }
- db_printf("\n");
- return 0;
-}
-
-void spc_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)
-#endif /* TRACE */
-
-#else /* DEBUG */
-#define PRINT(x)
-#define LOG(e,f)
-#define TR(x)
-#define TRCHECK
-#define TRWRAP
-#endif /* DEBUG */
-
-
-/*
- * Probe/Slave/Attach functions
- */
-
-/*
- * Probe routine:
- * Should find out (a) if the controller is
- * present and (b) which/where slaves are present.
- *
- * Implementation:
- * Send an identify msg to each possible target on the bus
- * except of course ourselves.
- */
-int spc_probe(reg, ui)
- char *reg;
- struct bus_ctlr *ui;
-{
- int tmp;
- int unit = ui->unit;
- spc_softc_t spc = &spc_softc_data[unit];
- int target_id, i;
- scsi_softc_t *sc;
- register spc_regmap_t *regs;
- int s;
- boolean_t did_banner = FALSE;
- char *cmd_ptr;
-
- /*
- * We are only called if the chip is there,
- * but make sure anyways..
- */
- regs = (spc_regmap_t *) (reg);
- if (check_memory((unsigned)regs, 0))
- return 0;
-
-#if notyet
- /* Mappable version side */
- SPC_probe(reg, ui);
-#endif
-
- /*
- * Initialize hw descriptor
- */
- spc_softc[unit] = spc;
- spc->regs = regs;
- spc->buff = spc_buffer;
-
- queue_init(&spc->waiting_targets);
-
- simple_lock_init(&spc->chiplock);
-
- sc = scsi_master_alloc(unit, (char*)spc);
- spc->sc = sc;
-
- sc->go = spc_go;
- sc->probe = spc_probe_target;
- sc->watchdog = scsi_watchdog;
- spc->wd.reset = spc_reset_scsibus;
-
-#ifdef MACH_KERNEL
- sc->max_dma_data = -1; /* unlimited */
-#else
- sc->max_dma_data = scsi_per_target_virtual;
-#endif
-
- scsi_might_disconnect[unit] = 0; /* XXX for now */
-
- /*
- * Reset chip
- */
- s = splbio();
- spc_reset(regs, TRUE);
- tmp = regs->spc_ints = regs->spc_ints;
-
- /*
- * Our SCSI id on the bus.
- */
-
- sc->initiator_id = bdid_to_id(regs->spc_bdid);
- printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id);
-
- /*
- * For all possible targets, see if there is one and allocate
- * a descriptor for it if it is there.
- */
- cmd_ptr = spc_buffer;
- for (target_id = 0; target_id < 8; target_id++, cmd_ptr += PER_TGT_BUFF_DATA) {
-
- register unsigned csr, ints;
- scsi_status_byte_t status;
-
- /* except of course ourselves */
- if (target_id == sc->initiator_id)
- continue;
-
- if (spc_select_target( regs, sc->initiator_id, target_id, FALSE)
- == SCSI_RET_DEVICE_DOWN) {
- tmp = regs->spc_ints = regs->spc_ints;
- continue;
- }
-
- printf(",%s%d", did_banner++ ? " " : " target(s) at ",
- target_id);
-
- /* should be command phase here: we selected wo ATN! */
- SPC_WAIT_PHASE(SCSI_PHASE_CMD);
-
- SPC_ACK(regs,SCSI_PHASE_CMD);
-
- /* build command in buffer */
- {
- unsigned char *p = (unsigned char*) cmd_ptr;
-
- p[0] = SCSI_CMD_TEST_UNIT_READY;
- p[1] =
- p[2] =
- p[3] =
- p[4] =
- p[5] = 0;
- }
-
- spc_data_out(regs, SCSI_PHASE_CMD, 6, cmd_ptr);
-
- SPC_WAIT_PHASE(SCSI_PHASE_STATUS);
-
- /* should have recieved a Command Complete Interrupt */
- while (!(regs->spc_ints))
- delay(1);
- ints = regs->spc_ints;
- if (ints != (SPC_INTS_DONE))
- gimmeabreak();
- regs->spc_ints = ints;
-
- SPC_ACK(regs,SCSI_PHASE_STATUS);
-
- csr = spc_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits);
- LOG(0x25,"din_count");
- LOG(0x80+csr,0);
-
- if (status.st.scsi_status_code != SCSI_ST_GOOD)
- scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0);
-
- /* expect command complete interupt */
- while (!(regs->spc_ints & SPC_INTS_DONE))
- delay(1);
-
- /* clear all intr bits */
- tmp = regs->spc_ints;
- LOG(0x26,"ints");
- LOG(0x80+tmp,0);
- regs->spc_ints = SPC_INTS_DONE;
-
- /* get cmd_complete message */
- SPC_WAIT_PHASE(SCSI_PHASE_MSG_IN);
-
- SPC_ACK(regs,SCSI_PHASE_MSG_IN);
-
- csr = spc_data_in(regs,SCSI_PHASE_MSG_IN, 1,(unsigned char*)&i);
- LOG(0x25,"din_count");
- LOG(0x80+csr,0);
-
- while (!(regs->spc_ints & SPC_INTS_DONE))
- delay(1);
-
- /* clear all done intr */
- tmp = regs->spc_ints;
- LOG(0x26,"ints");
- LOG(0x80+tmp,0);
- regs->spc_ints = SPC_INTS_DONE;
-
- SPC_ACK(regs,SPC_PHASE_DISC);
-
- /* release the bus */
- regs->spc_pctl = ~SPC_PCTL_BFREE_IE & SPC_PHASE_DISC;
- /* regs->spc_scmd = 0; only in TARGET mode */
-
- /* wait for disconnected interrupt */
- while (!(regs->spc_ints & SPC_INTS_DISC))
- delay(1);
-
- tmp = regs->spc_ints;
- LOG(0x26,"ints");
- LOG(0x80+tmp,0);
- regs->spc_ints = tmp;
- LOG(0x29,"Probed\n");
-
- /*
- * Found a target
- */
- spc->ntargets++;
- {
- register target_info_t *tgt;
-
- tgt = scsi_slave_alloc(unit, target_id, (char*)spc);
-
- /* "virtual" address for our use */
- tgt->cmd_ptr = cmd_ptr;
- /* "physical" address for dma engine (??) */
- tgt->dma_ptr = 0;
-#ifdef MACH_KERNEL
-#else /*MACH_KERNEL*/
- fdma_init(&tgt->fdma, scsi_per_target_virtual);
-#endif /*MACH_KERNEL*/
- }
- }
- printf(".\n");
-
- splx(s);
- return 1;
-}
-
-boolean_t
-spc_probe_target(tgt, ior)
- target_info_t *tgt;
- io_req_t ior;
-{
- boolean_t newlywed;
-
- newlywed = (tgt->cmd_ptr == 0);
- if (newlywed) {
- /* desc was allocated afresh */
-
- /* "virtual" address for our use */
- tgt->cmd_ptr = &spc_buffer[PER_TGT_BUFF_DATA*tgt->target_id +
- (tgt->masterno*8*PER_TGT_BUFF_DATA) ];
- /* "physical" address for dma engine */
- tgt->dma_ptr = 0;
-#ifdef MACH_KERNEL
-#else /*MACH_KERNEL*/
- fdma_init(&tgt->fdma, scsi_per_target_virtual);
-#endif /*MACH_KERNEL*/
-
- }
-
- if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN)
- return FALSE;
-
- tgt->flags = TGT_ALIVE;
- return TRUE;
-}
-
-int bdid_to_id(bdid)
- register int bdid;
-{
- register int i;
- for (i = 0; i < 8; i++)
- if (bdid == (1 << i)) break;
- return i;
-}
-
-scsi_ret_t
-spc_select_target(regs, myid, id, with_atn)
- register spc_regmap_t *regs;
- unsigned myid, id;
- boolean_t with_atn;
-{
- scsi_ret_t ret = SCSI_RET_RETRY;
- int mask;
-
- if ((regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
-#ifdef MIPS
- && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
- && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
-#endif
- )
- return ret;
-
- /* setup for for select:
-
-#if 0
- (1) Toggle the Enable transfer bit (turning on the chips
- SCSI bus drivers).
-#endif
- (2) Enable arbitration, parity, reselect display, but
- disable interrupt generation to the CPU (we are polling).
- (3) Disable the bus free interrupt and set I/O direction
- (4) If doing a select with attention, write the Set attention command.
- Then delay 1 microsecond to avoid command races.
-
- (5) Temp register gets 1<<target | 1<<initiator ids
- (6) Timeout clocked into transfer registers
- (7) Drive select (and optionally attention) onto the bus
- (8) Wait 1/4 second for timeout.
- */
-
-#if 0
- regs->spc_psns = SPC_DIAG_ENBL_XFER; /* (1) */
-#endif
-
- regs->spc_sctl = SPC_SCTL_ARB_EBL|
- SPC_SCTL_PAR_EBL|
- SPC_SCTL_RSEL_EBL; /* (2) */
-
-
-
- mask = ~SPC_PCTL_BFREE_IE & regs->spc_pctl;
- mask &= ~1; /* set I/O direction to be out */
-
- regs->spc_pctl = mask; /* (3) */
-
- if (with_atn)
- {
- regs->spc_scmd = SPC_SCMD_C_ATN_S; /* (4) */
- delay(1);
- }
-
- regs->spc_temp = (1<<myid) | (1<<id); /* (5) */
-
- SPC_TC_PUT(regs,0xfa004); /* (6) */
-
- regs->spc_scmd = (SPC_SCMD_C_SELECT | SPC_SCMD_PROGRAMMED_X); /* (7) */
-
- {
- int count = 2500;
-
- /* wait for an interrupt */
- while ((regs->spc_ints)==0)
- {
- if (--count > 0)
- delay(100);
- else
- {
- goto nodev;
- }
- }
-
- count = regs->spc_ints;
- if (count & SPC_INTS_TIMEOUT)
- {
- /* sanity check. The ssts should have the busy bit set */
- if (regs->spc_ssts & SPC_SSTS_BUSY)
- goto nodev;
- else
- panic("spc_select_target: timeout");
- }
-
- /* otherwise, we should have received a
- command complete interrupt */
-
- if (count & ~SPC_INTS_DONE)
- panic("spc_select_target");
-
- } /* (8) */
-
- /* we got a response - now connected; bus is in COMMAND phase */
-
- regs->spc_ints = regs->spc_ints;
- /* regs->spc_scmd = 0; target only */
- return SCSI_RET_SUCCESS;
-nodev:
- SPC_TC_PUT(regs,0); /* play it safe */
- regs->spc_ints = regs->spc_ints;
- /* regs->spc_scmd = 0; target only */
- ret = SCSI_RET_DEVICE_DOWN;
- return ret;
-}
-
-int spc_data_out(regs, phase, count, data)
- int phase, count;
- register spc_regmap_t *regs;
- unsigned char *data;
-{
- /* This is the one that sends data out. returns how many
- bytes it did NOT xfer: */
-
- if (SPC_CUR_PHASE(regs->spc_phase) != phase)
- return count;
-
- /* check that the fifo is empty. If not, cry */
- if (!(regs->spc_ssts & SPC_SSTS_FIFO_EMPTY))
- panic("spc_data_out: junk in fifo\n");
-
- SPC_TC_PUT(regs,count);
- regs->spc_scmd = SPC_SCMD_C_XFER | SPC_SCMD_PROGRAMMED_X;
-
- /* wait for the SPC to start processing the command */
- while ((regs->spc_ssts & (SPC_SSTS_INI_CON|SPC_SSTS_TGT_CON|SPC_SSTS_BUSY|SPC_SSTS_XIP))
- != (SPC_SSTS_INI_CON|SPC_SSTS_BUSY|SPC_SSTS_XIP))
- delay(1);
-
- /* shovel out the data */
-
- while (count)
- {
- /* check if interrupt is pending */
- int ints = regs->spc_ints;
- int ssts;
-
- if (ints) /* something has gone wrong */
- break;
-
- ssts = regs->spc_ssts;
- if (ssts & SPC_SSTS_FIFO_FULL) /* full fifo - can't write */
- delay(1);
- else
- { /* spit out a byte */
- regs->spc_dreg = *data;
- data++;
- count--;
- }
- }
-
-
- if (count != 0)
- {
- /* need some sort of fifo cleanup if failed */
- gimmeabreak(); /* Bytes stranded in the fifo */
- }
-
- return count;
-}
-
-int spc_data_in(regs, phase, count, data)
- int phase, count;
- register spc_regmap_t *regs;
- unsigned char *data;
-{
- if (SPC_CUR_PHASE(regs->spc_phase) != phase)
- return count;
-
- SPC_TC_PUT(regs,count);
- regs->spc_scmd = SPC_SCMD_C_XFER | SPC_SCMD_PROGRAMMED_X;
-
- /* The Fujistu code sample suggests waiting for the top nibble of the SSTS to
- become 0xb (ssts & 0xf0) = 0xb. This state, however is transient. If the
- message is short (say , 1 byte), it can get sucked into the fifo before
- we ever get to look at the state. So instead, we are going to wait for
- the fifo to become nonempty.
- */
-
- while ((regs->spc_ssts & SPC_SSTS_FIFO_EMPTY))
- delay(1);
-
- while (count)
- {
- int ints = regs->spc_ints;
- int ssts;
-
- /* If there is an interrupt pending besides command complete or
- phase mismatch, give up */
-
- if (ints & ~(SPC_INTS_DONE|SPC_INTS_BUSREQ))
- break;
-
- /* see if there is any data in the fifo */
- ssts = regs->spc_ssts;
- if ((ssts & SPC_SSTS_FIFO_EMPTY) == 0)
- {
- *data = regs->spc_dreg;
- data++;
- count--;
- continue;
- }
-
- /* if empty, check if phase has changed */
- if (SPC_CUR_PHASE(regs->spc_phase) != phase)
- break;
-
- }
-
- if ((count==0) && (phase == SCSI_PHASE_MSG_IN))
- {
- while (!(regs->spc_ints & SPC_INTS_DONE))
- delay(1);
-
- /*
- So the command complete interrupt has arrived. Now check that the
- other two conditions we expect - The psns to be in ack|busy|message_in phase
- and ssts to indicate connected|xfer in progress|busy|xfer counter 0|empty fifo
- are true.
- */
- while (1)
- {
- register int psns = regs->spc_psns;
- register int ssts = regs->spc_ssts;
- register int sscon = ssts & (SPC_SSTS_INI_CON | SPC_SSTS_TGT_CON);
- register int ssncon = ssts & ~(SPC_SSTS_INI_CON | SPC_SSTS_TGT_CON);
-
- if (psns == (SPC_BUS_ACK | SPC_BUS_BSY | SCSI_PHASE_MSG_IN) &&
- ssncon == (SPC_SSTS_BUSY | SPC_SSTS_XIP | SPC_SSTS_TC0 | SPC_SSTS_FIFO_EMPTY) &&
- sscon)
- break;
- }
-
- regs->spc_scmd = SPC_SCMD_C_ACKREQ_C;
- }
-
- return count;
-}
-
-void spc_reset(regs, quickly)
- register spc_regmap_t *regs;
- boolean_t quickly;
-{
- register char myid;
-
- /* save our id across reset */
- myid = bdid_to_id(regs->spc_bdid);
-
- /* wait for Reset In signal to go low */
- while (regs->spc_ssts & SPC_SSTS_RST)
- delay(1);
-
- /* reset chip */
- regs->spc_sctl = SPC_SCTL_RESET;
- delay(25);
-
- regs->spc_myid = myid;
- regs->spc_sctl = SPC_SCTL_ARB_EBL|SPC_SCTL_PAR_EBL|SPC_SCTL_SEL_EBL|
- SPC_SCTL_RSEL_EBL|SPC_SCTL_IE;
- regs->spc_scmd = SPC_SCMD_C_BUS_RLSE;
- /* regs->spc_tmod = 0; - SANDRO ? */
- regs->spc_ints = 0xff;/* clear off any pending */
-#if 0
- regs->spc_pctl = SPC_PCTL_LST_IE; /* useful only on 87033 */
-#else
- regs->spc_pctl = 0;
-#endif
- regs->spc_mbc = 0;
- SPC_TC_PUT(regs,0);
-
- if (quickly)
- return;
-
- /*
- * reset the scsi bus, the interrupt routine does the rest
- * or you can call spc_bus_reset().
- */
- regs->spc_scmd = SPC_SCMD_BUSRST|SPC_SCMD_C_STOP_X;/*?*/
-}
-
-/*
- * Operational functions
- */
-
-/*
- * Start a SCSI command on a target
- */
-spc_go(tgt, cmd_count, in_count, cmd_only)
- int cmd_count, in_count;
- target_info_t *tgt;
- boolean_t cmd_only;
-{
- spc_softc_t spc;
- register int s;
- boolean_t disconn;
- script_t scp;
- boolean_t (*handler)();
- int late;
-
- LOG(1,"\n\tgo");
-
- spc = (spc_softc_t)tgt->hw_state;
-
- /*
- * We cannot do real DMA.
- */
-#ifdef MACH_KERNEL
-#else /*MACH_KERNEL*/
- if (tgt->ior)
- fdma_map(&tgt->fdma, tgt->ior);
-#endif /*MACH_KERNEL*/
-
- 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;
- tgt->transient_state.copy_count = 0;
-
- if (len < tgt->block_size) {
- gimmeabreak();
-
- /* avoid leaks */
-#if 0
-you`ll have to special case this
-#endif
- tgt->transient_state.out_count = tgt->block_size;
- }
- } else {
- tgt->transient_state.out_count = 0;
- tgt->transient_state.copy_count = 0;
- }
-
- tgt->transient_state.cmd_count = cmd_count;
-
- disconn =
- BGET(scsi_might_disconnect,(unsigned)tgt->masterno, tgt->target_id);
- disconn = disconn && (spc->ntargets > 1);
- disconn |=
- BGET(scsi_should_disconnect,(unsigned)tgt->masterno, tgt->target_id);
-
- /*
- * Setup target state
- */
- tgt->done = SCSI_RET_IN_PROGRESS;
-
- handler = (disconn) ? spc_err_disconn : spc_err_generic;
-
- /* determine wether or not to use the late forms of the scripts */
- late = cmd_only ? FALSE : (tgt->flags & TGT_DID_SYNCH);
-
- switch (tgt->cur_cmd) {
- case SCSI_CMD_READ:
- case SCSI_CMD_LONG_READ:
- LOG(0x13,"readop");
- scp = late ? spc_script_late_data_in : spc_script_data_in;
- break;
- case SCSI_CMD_WRITE:
- case SCSI_CMD_LONG_WRITE:
- LOG(0x14,"writeop");
- scp = late ? spc_script_late_data_out : spc_script_data_out;
- break;
- case SCSI_CMD_INQUIRY:
- /* This is likely the first thing out:
- do the synch neg if so */
- if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) {
- scp = spc_script_try_synch;
- tgt->flags |= TGT_TRY_SYNCH;
- break;
- }
- 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 */
- scp = late ? spc_script_late_data_in : spc_script_data_in;
- LOG(0x1c,"cmdop");
- LOG(0x80+tgt->cur_cmd,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 */
- tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd);
- tgt->transient_state.out_count =
- cmd_count - tgt->transient_state.cmd_count;
- scp = late ? spc_script_late_data_out : spc_script_data_out;
- LOG(0x1c,"cmdop");
- LOG(0x80+tgt->cur_cmd,0);
- break;
- case SCSI_CMD_TEST_UNIT_READY:
- /*
- * Do the synch negotiation here, unless prohibited
- * or done already
- */
- if (tgt->flags & TGT_DID_SYNCH) {
- scp = late ? spc_script_late_cmd : spc_script_cmd;
- } else {
- scp = spc_script_try_synch;
- tgt->flags |= TGT_TRY_SYNCH;
- cmd_only = FALSE;
- }
- LOG(0x1c,"cmdop");
- LOG(0x80+tgt->cur_cmd,0);
- break;
- default:
- LOG(0x1c,"cmdop");
- LOG(0x80+tgt->cur_cmd,0);
- scp = late ? spc_script_late_cmd : spc_script_cmd;
- }
-
- tgt->transient_state.script = scp;
- tgt->transient_state.handler = handler;
- tgt->transient_state.identify = (cmd_only) ? 0xff :
- (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT :
- SCSI_IDENTIFY);
-
- if (in_count)
- tgt->transient_state.in_count =
- (in_count < tgt->block_size) ? tgt->block_size : in_count;
- else
- tgt->transient_state.in_count = 0;
- tgt->transient_state.dma_offset = 0;
-
- /*
- * See if another target is currently selected on
- * this SCSI bus, e.g. lock the spc structure.
- * Note that it is the strategy routine's job
- * to serialize ops on the same target as appropriate.
- */
-#if 0
-locking code here
-#endif
- s = splbio();
-
- if (spc->wd.nactive++ == 0)
- spc->wd.watchdog_state = SCSI_WD_ACTIVE;
-
- if (spc->state & SPC_STATE_BUSY) {
- /*
- * Queue up this target, note that this takes care
- * of proper FIFO scheduling of the scsi-bus.
- */
- LOG(3,"enqueue");
- enqueue_tail(&spc->waiting_targets, (queue_entry_t) tgt);
- } else {
- /*
- * It is down to at most two contenders now,
- * we will treat reconnections same as selections
- * and let the scsi-bus arbitration process decide.
- */
- spc->state |= SPC_STATE_BUSY;
- spc->next_target = tgt;
- spc_attempt_selection(spc);
- /*
- * Note that we might still lose arbitration..
- */
- }
- splx(s);
-}
-
-void spc_attempt_selection(spc)
- spc_softc_t spc;
-{
- target_info_t *tgt;
- spc_regmap_t *regs;
- register int cmd;
- int atn=0;
-
- /* This is about your select code */
-
- regs = spc->regs;
- tgt = spc->next_target;
-
- LOG(4,"select");
- LOG(0x80+tgt->target_id,0);
-
- /*
- * Init bus state variables and set registers.
- */
- spc->active_target = tgt;
-
- /* reselection pending ? */
- if ((regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
-#ifdef MIPS
- && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
- && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL))
-#endif
- )
- return;
-
- spc->script = tgt->transient_state.script;
- spc->error_handler = tgt->transient_state.handler;
- spc->done = SCSI_RET_IN_PROGRESS;
-
- spc->in_count = 0;
- spc->out_count = 0;
-
- cmd = SPC_SCMD_C_SELECT | SPC_SCMD_PROGRAMMED_X;
- if (tgt->flags & TGT_DID_SYNCH)
- {
- if (tgt->transient_state.identify != 0xff)
- atn = 1;
- }
- else
- if (tgt->flags & TGT_TRY_SYNCH)
- atn = 1;
-
-#if 0
- regs->spc_psns = SPC_DIAG_ENBL_XFER;
-#endif
-
- regs->spc_sctl = SPC_SCTL_ARB_EBL | SPC_SCTL_PAR_EBL |
- SPC_SCTL_RSEL_EBL | SPC_SCTL_IE;
-
-
- { int mask;
- mask = ~SPC_PCTL_BFREE_IE & regs->spc_pctl;
- regs->spc_pctl = mask;
- }
-
- regs->spc_temp = (1<<(spc->sc->initiator_id)) | (1<<(tgt->target_id));
-
- SPC_TC_PUT(regs,0xfa004);
-
- if (atn)
- {
- regs->spc_scmd = SPC_SCMD_C_ATN_S;
- /* delay 1us to avoid races */
- delay(1);
- }
-
- regs->spc_scmd = cmd;
- return;
-}
-
-/*
- * Interrupt routine
- * Take interrupts from the chip
- *
- * Implementation:
- * Move along the current command's script if
- * all is well, invoke error handler if not.
- */
-void spc_intr(unit)
-int unit;
-{
- register spc_softc_t spc;
- register script_t scp;
- register unsigned ints, psns, ssts;
- register spc_regmap_t *regs;
- boolean_t try_match;
-#if notyet
- extern boolean_t rz_use_mapped_interface;
-
- if (rz_use_mapped_interface)
- {
- SPC_intr(unit);
- return;
- }
-#endif
-
- spc = spc_softc[unit];
- regs = spc->regs;
-
- /* read the interrupt status register */
- ints = regs->spc_ints;
-
- LOG(5,"\n\tintr");
- LOG(0x80+ints,0);
-
-TR(ints);
-TRCHECK;
-
- if (ints & SPC_INTS_RESET)
- {
- /* does its own interrupt reset when ready */
- spc_bus_reset(spc);
- return;
- }
-
- /* we got an interrupt allright */
- if (spc->active_target)
- spc->wd.watchdog_state = SCSI_WD_ACTIVE;
-
-
- if (ints == 0)
- { /* no obvious cause */
- LOG(2,"SPURIOUS");
- gimmeabreak();
- return;
- }
-
-
- /* reset the interrupt */
- regs->spc_ints = ints;
-
- /* go get the phase, and status. We can't trust the
- phase until REQ is asserted in the psns. Only do
- this is we received a command complete or service
- required interrupt. Otherwise, just read them once
- and trust. */
-
-
-
- if (ints & (SPC_INTS_DONE|SPC_INTS_BUSREQ))
- while(1)
- {
- psns = regs->spc_psns;
- if (psns & SPC_BUS_REQ)
- break;
- delay(1); /* don't hog the bus */
- }
- else
- psns = regs->spc_psns;
-
- ssts = regs->spc_psns;
-
-TR(psns);
-TR(ssts);
-TRCHECK;
-
- if ((spc->state & SPC_STATE_TARGET) ||
- (ints & SPC_INTS_SELECTED))
- spc_target_intr(spc /**, ints, psns, ssts **/);
-
- scp = spc->script;
-
- if ((scp == 0) || (ints & SPC_INTS_RESELECTED))
- {
- gimmeabreak();
- spc_reconnect(spc, ints, psns, ssts);
- return;
- }
-
- if (SCRIPT_MATCH(psns) != scp->condition) {
- if (try_match = (*spc->error_handler)(spc, ints, psns, ssts)) {
- psns = regs->spc_psns;
- ssts = regs->spc_ssts;
- }
- } else
- try_match = TRUE;
-
-
- /* might have been side effected */
- scp = spc->script;
-
- if (try_match && (SCRIPT_MATCH(psns) == scp->condition)) {
- /*
- * Perform the appropriate operation,
- * then proceed
- */
- if ((*scp->action)(spc, ints, psns, ssts)) {
- /* might have been side effected */
- scp = spc->script;
- spc->script = scp + 1;
- }
- }
-}
-
-void spc_target_intr(spc)
- register spc_softc_t spc;
-{
- panic("SPC: TARGET MODE !!!\n");
-}
-
-/*
- * All the many little things that the interrupt
- * routine might switch to
- */
-boolean_t
-spc_issue_command(spc, ints, psns, ssts)
- spc_softc_t spc;
- int ints, psns, ssts;
-{
- register spc_regmap_t *regs = spc->regs;
-
- LOG(0x12, "cmd_issue");
- /* we have just done a select;
- Bus is in CMD phase;
- need to phase match */
- SPC_ACK(regs, SCSI_PHASE_CMD);
-
- return spc_data_out(regs, SCSI_PHASE_CMD,
- spc->active_target->transient_state.cmd_count,
- spc->active_target->cmd_ptr) ? FALSE : TRUE;
-}
-
-boolean_t
-spc_issue_ident_and_command(spc, ints, psns, ssts)
- spc_softc_t spc;
- int ints, psns, ssts;
-{
- register spc_regmap_t *regs = spc->regs;
-
- LOG(0x22, "ident_and_cmd");
- /* we have just done a select with atn Bus is in MSG_OUT phase;
- need to phase match */
- SPC_ACK(regs, SCSI_PHASE_MSG_OUT);
-
- spc_data_out(regs, SCSI_PHASE_MSG_OUT, 1,
- &spc->active_target->transient_state.identify);
-
- /* wait to go to command phase */
- SPC_WAIT_PHASE(SCSI_PHASE_CMD);
-
- /* ack */
- SPC_ACK(regs, SCSI_PHASE_CMD);
-
- /* should be a command complete intr pending. Eat it */
- if (regs->spc_ints != SPC_INTS_DONE)
- gimmeabreak();
- regs->spc_ints = SPC_INTS_DONE;
-
- /* spit */
- return spc_data_out(regs, SCSI_PHASE_CMD,
- spc->active_target->transient_state.cmd_count,
- spc->active_target->cmd_ptr) ? FALSE : TRUE;
-}
-
-
-boolean_t
-spc_end_transaction( spc, ints, psns, serr)
- register spc_softc_t spc;
- int ints, psns, serr;
-{
- register spc_regmap_t *regs = spc->regs;
- char cmc;
- int tmp;
-
- LOG(0x1f,"end_t");
-
- SPC_ACK(regs,SCSI_PHASE_MSG_IN /*,1*/);
-
- spc_data_in(regs, SCSI_PHASE_MSG_IN, 1, &cmc);
-
- if (cmc != SCSI_COMMAND_COMPLETE)
- printf("{T%x}", cmc);
-
- while (regs->spc_ints != (SPC_INTS_DONE|SPC_INTS_DISC));
-
- SPC_ACK(regs,SPC_PHASE_DISC);
-
- /* going to disconnect */
- regs->spc_pctl = ~SPC_PCTL_BFREE_IE & SPC_PHASE_DISC;
- /* regs->spc_scmd = 0; */
-
- /* clear all intr bits? */
- tmp = regs->spc_ints;
- regs->spc_ints = tmp;
-
-
- if (!spc_end(spc, ints, psns, serr))
- (void) spc_reconnect(spc, ints, psns, serr);
- return FALSE;
-}
-
-boolean_t
-spc_end( spc, ints, psns, serr)
- register spc_softc_t spc;
- int ints, psns, serr;
-{
- register target_info_t *tgt;
- register io_req_t ior;
- register spc_regmap_t *regs = spc->regs;
- int csr;
-
- LOG(6,"end");
-
- tgt = spc->active_target;
-
- if ((tgt->done = spc->done) == SCSI_RET_IN_PROGRESS)
- tgt->done = SCSI_RET_SUCCESS;
-
- spc->script = 0;
-
- if (spc->wd.nactive-- == 1)
- spc->wd.watchdog_state = SCSI_WD_INACTIVE;
-
- /* check reconnection not pending */
- csr = SPC_INTS_RESELECTED & regs->spc_ints;
- if (!csr)
- spc_release_bus(spc);
- else
- {
- spc->active_target = 0;
- /* spc->state &= ~SPC_STATE_BUSY; later */
- }
- if (ior = tgt->ior) {
-#ifdef MACH_KERNEL
-#else /*MACH_KERNEL*/
- fdma_unmap(&tgt->fdma, ior);
-#endif /*MACH_KERNEL*/
- LOG(0xA,"ops->restart");
- (*tgt->dev_ops->restart)( tgt, TRUE);
- if (csr)
- spc->state &= ~SPC_STATE_BUSY;
- }
-
- /* return not reselected */
- return (csr & SPC_INTS_RESELECTED) ? 0 : 1;
-}
-
-boolean_t
-spc_release_bus(spc)
- register spc_softc_t spc;
-{
- boolean_t ret = FALSE;
-
- LOG(9,"release");
-
- spc->script = 0;
-
- if (spc->state & SPC_STATE_COLLISION) {
-
- LOG(0xB,"collided");
- spc->state &= ~SPC_STATE_COLLISION;
- spc_attempt_selection(spc);
-
- } else if (queue_empty(&spc->waiting_targets)) {
-
- spc->state &= ~SPC_STATE_BUSY;
- spc->active_target = 0;
- ret = TRUE;
-
- } else {
-
- LOG(0xC,"dequeue");
- spc->next_target = (target_info_t *)
- dequeue_head(&spc->waiting_targets);
- spc_attempt_selection(spc);
- }
- return ret;
-}
-
-boolean_t
-spc_get_status( spc, ints, psns, serr)
- register spc_softc_t spc;
- int ints, psns, serr;
-{
- register spc_regmap_t *regs = spc->regs;
- scsi2_status_byte_t status;
- register target_info_t *tgt;
-
- LOG(0xD,"get_status");
-TRWRAP;
-
- spc->state &= ~SPC_STATE_DMA_IN;
-
- tgt = spc->active_target;
-
- SPC_ACK(regs,SCSI_PHASE_STATUS /*,1*/);
-
- spc_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits);
-
- if (status.st.scsi_status_code != SCSI_ST_GOOD) {
- scsi_error(spc->active_target, SCSI_ERR_STATUS, status.bits, 0);
- spc->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ?
- SCSI_RET_RETRY : SCSI_RET_NEED_SENSE;
- } else
- spc->done = SCSI_RET_SUCCESS;
-
- return TRUE;
-}
-
-boolean_t
-spc_xfer_in( spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- register target_info_t *tgt;
- register spc_regmap_t *regs = spc->regs;
- register int count;
- boolean_t advance_script = TRUE;
-
- LOG(0xE,"xfer_in");
-
- tgt = spc->active_target;
- spc->state |= SPC_STATE_DMA_IN;
-
- count = tgt->transient_state.in_count;
-
- SPC_ACK(regs, SCSI_PHASE_DATAI);
-
- if ((tgt->cur_cmd != SCSI_CMD_READ) &&
- (tgt->cur_cmd != SCSI_CMD_LONG_READ))
- spc_data_in(regs, SCSI_PHASE_DATAI, count, tgt->cmd_ptr);
- else
- {
- spc_data_in(regs, SCSI_PHASE_DATAI, count, tgt->ior->io_data);
- }
-
- return advance_script;
-}
-
-boolean_t
-spc_xfer_out( spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- register spc_regmap_t *regs = spc->regs;
- register target_info_t *tgt;
- boolean_t advance_script = TRUE;
- int count = spc->out_count;
-
- LOG(0xF,"xfer_out");
-
- tgt = spc->active_target;
- spc->state &= ~SPC_STATE_DMA_IN;
-
- count = tgt->transient_state.out_count;
-
- SPC_ACK(regs, SCSI_PHASE_DATAO);
-
- if ((tgt->cur_cmd != SCSI_CMD_WRITE) &&
- (tgt->cur_cmd != SCSI_CMD_LONG_WRITE))
- spc_data_out(regs, SCSI_PHASE_DATAO, count,
- tgt->cmd_ptr + tgt->transient_state.cmd_count);
- else
- spc_data_out(regs, SCSI_PHASE_DATAO, count, tgt->ior->io_data);
-
- return advance_script;
-}
-
-/* disconnect-reconnect ops */
-
-/* get the message in via dma ?? */
-boolean_t
-spc_msg_in(spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- register target_info_t *tgt;
-
- LOG(0x15,"msg_in");
- gimmeabreak();
-
- tgt = spc->active_target;
-
-#if 0
-You can do this by hand, just leave an interrupt pending at the end
-#endif
-
- /* We only really expect two bytes */
-#if 0
- SPC_PUT(dmar,sizeof(scsi_command_group_0));
- ....
-#endif
- return TRUE;
-}
-
-/* check the message is indeed a DISCONNECT */
-boolean_t
-spc_disconnect(spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- register int len = 0;
- boolean_t ok = FALSE;
- register char *msgs = 0;
-
-
-/* SPC_TC_GET(dmar,len); */
- len = sizeof(scsi_command_group_0) - len;
-
-/* msgs = tgt->cmd_ptr; */ /* I think */
-
- if ((len == 0) || (len > 2) || msgs == 0)
- ok = FALSE;
- else {
- /* A SDP message preceeds it in non-completed READs */
- ok = ((msgs[0] == SCSI_DISCONNECT) || /* completed op */
- ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* incomplete */
- (msgs[1] == SCSI_DISCONNECT)));
- }
- if (!ok)
- printf("[tgt %d bad msg (%d): %x]",
- spc->active_target->target_id, len, *msgs);
-
- return TRUE;
-}
-
-/* save all relevant data, free the BUS */
-boolean_t
-spc_disconnected(spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- register target_info_t *tgt;
-
-/* make sure reselects will work */
-
- LOG(0x16,"disconnected");
-
- spc_disconnect(spc,ints, psns, ssts);
-
- tgt = spc->active_target;
- tgt->flags |= TGT_DISCONNECTED;
- tgt->transient_state.handler = spc->error_handler;
- /* the rest has been saved in spc_err_disconn() */
-
- PRINT(("{D%d}", tgt->target_id));
-
- spc_release_bus(spc);
-
- return FALSE;
-}
-
-/* get reconnect message, restore BUS */
-boolean_t
-spc_reconnect(spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
-
- LOG(0x17,"reconnect");
-
- if (spc->wd.nactive == 0) {
- LOG(2,"SPURIOUS");
- return FALSE;
- }
-
-#if 0
-This is the 5380 code, for reference:
- spc_regmap_t *regs = spc->regs;
- register target_info_t *tgt;
- register int id;
- int msg;
-
-
- id = regs->spc_data;/*parity?*/
- /* xxx check our id is in there */
-
- id &= ~(1 << spc->sc->initiator_id);
- {
- register int i;
- for (i = 0; i < 8; i++)
- if (id & (1 << i)) break;
-if (i == 8) {printf("{P%x}", id);return;}
- id = i;
- }
- regs->spc_icmd = SPC_ICMD_BSY;
- while (regs->spc_bus_csr & SPC_BUS_SEL)
- ;
- regs->spc_icmd = 0;
- delay_1p2_us();
- while ( ((regs->spc_bus_csr & SPC_BUS_BSY) == 0) &&
- ((regs->spc_bus_csr & SPC_BUS_BSY) == 0) &&
- ((regs->spc_bus_csr & SPC_BUS_BSY) == 0))
- ;
-
- /* Now should wait for correct phase: REQ signals it */
- while ( ((regs->spc_bus_csr & SPC_BUS_REQ) == 0) &&
- ((regs->spc_bus_csr & SPC_BUS_REQ) == 0) &&
- ((regs->spc_bus_csr & SPC_BUS_REQ) == 0))
- ;
-
- regs->spc_mode |= SPC_MODE_MONBSY;
-
- /*
- * See if this reconnection collided with a selection attempt
- */
- if (spc->state & SPC_STATE_BUSY)
- spc->state |= SPC_STATE_COLLISION;
-
- spc->state |= SPC_STATE_BUSY;
-
- /* Get identify msg */
- bs = regs->spc_phase;
-if (SPC_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) gimmeabreak();
- SPC_ACK(regs,SCSI_PHASE_MSG_IN /*,1*/);
- msg = 0;
- spc_data_in(regs, SCSI_PHASE_MSG_IN, 1, &msg);
- regs->spc_mode = SPC_MODE_PAR_CHK|SPC_MODE_DMA|SPC_MODE_MONBSY;
-
- if (msg != SCSI_IDENTIFY)
- printf("{I%x %x}", id, msg);
-
- tgt = spc->sc->target[id];
- if (id > 7 || tgt == 0) panic("spc_reconnect");
-
- PRINT(("{R%d}", id));
- if (spc->state & SPC_STATE_COLLISION)
- PRINT(("[B %d-%d]", spc->active_target->target_id, id));
-
- LOG(0x80+id,0);
-
- spc->active_target = tgt;
- tgt->flags &= ~TGT_DISCONNECTED;
-
- spc->script = tgt->transient_state.script;
- spc->error_handler = tgt->transient_state.handler;
- spc->in_count = 0;
- spc->out_count = 0;
-
- /* Should get a phase mismatch when tgt changes phase */
-#endif
- return TRUE;
-}
-
-
-
-/* do the synch negotiation */
-boolean_t
-spc_dosynch( spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- /*
- * Phase is MSG_OUT here, cmd has not been xferred
- */
- int len;
- register target_info_t *tgt;
- register spc_regmap_t *regs = spc->regs;
- unsigned char off;
- unsigned char p[6];
-
- LOG(0x11,"dosync");
-
- /* ATN still asserted */
- SPC_ACK(regs,SCSI_PHASE_MSG_OUT);
-
- tgt = spc->active_target;
-
- tgt->flags |= TGT_DID_SYNCH; /* only one chance */
- tgt->flags &= ~TGT_TRY_SYNCH;
-
- /*p = some scratch buffer, on the stack */
-
- p[0] = SCSI_IDENTIFY;
- p[1] = SCSI_EXTENDED_MESSAGE;
- p[2] = 3;
- p[3] = SCSI_SYNC_XFER_REQUEST;
- /* We cannot run synchronous */
-#define spc_to_scsi_period(x) 0x7
-#define scsi_period_to_spc(x) (x)
- off = 0;
- p[4] = spc_to_scsi_period(spc_min_period);
- p[5] = off;
-
- /* The transfer is started with ATN still set. The
- chip will automagically drop ATN before it transfers the
- last byte. Pretty neat. */
- spc_data_out(regs, SCSI_PHASE_MSG_OUT,
- sizeof(scsi_synch_xfer_req_t)+1, p);
-
- /* wait for phase change to status phase */
- SPC_WAIT_PHASE_VANISH(SCSI_PHASE_MSG_OUT);
-
-
- psns = regs->spc_phase;
-
- /* The standard sez there nothing else the target can do but.. */
- if (SPC_CUR_PHASE(psns) != SCSI_PHASE_MSG_IN)
- panic("spc_dosync");/* XXX put offline */
-
- /*
- msgin:
- */
- /* ack */
- SPC_ACK(regs,SCSI_PHASE_MSG_IN);
-
- /* clear any pending interrupts */
- regs->spc_ints = regs->spc_ints;
-
- /* get answer */
- len = sizeof(scsi_synch_xfer_req_t);
- len = spc_data_in(regs, SCSI_PHASE_MSG_IN, len, p);
-
- /* do not cancel the phase mismatch interrupt ! */
-
- /* look at the answer and see if we like it */
- if (len || (p[0] != SCSI_EXTENDED_MESSAGE)) {
- /* did not like it at all */
- printf(" did not like SYNCH xfer ");
- } else {
- /* will NOT do synch */
- printf(" but we cannot do SYNCH xfer ");
- tgt->sync_period = scsi_period_to_spc(p[3]);
- tgt->sync_offset = p[4];
- /* sanity */
- if (tgt->sync_offset != 0)
- printf(" ?OFFSET %x? ", tgt->sync_offset);
- }
-
- /* wait for phase change */
- SPC_WAIT_PHASE_VANISH(SCSI_PHASE_MSG_IN);
-
- psns = regs->spc_phase;
-
- /* phase should be command now */
- /* continue with simple command script */
- spc->error_handler = spc_err_generic;
- spc->script = spc_script_cmd;
-
-/* Make sure you get out right here, esp the script pointer and/or pending intr */
-
- if (SPC_CUR_PHASE(psns) == SCSI_PHASE_CMD )
- return FALSE;
-
- if (SPC_CUR_PHASE(psns) == SCSI_PHASE_STATUS ) /* jump to get_status */
- return TRUE; /* intr is pending */
-
- spc->script++;
- if (SPC_CUR_PHASE(psns) == SCSI_PHASE_MSG_IN )
- return TRUE;
-
- if ((psns & SPC_BUS_BSY) == 0) /* uhu? disconnected */
- return TRUE;
-
- gimmeabreak();
- return FALSE;
-}
-
-/*
- * The bus was reset
- */
-void spc_bus_reset(spc)
- register spc_softc_t spc;
-{
- register spc_regmap_t *regs = spc->regs;
-
- LOG(0x21,"bus_reset");
-
- /*
- * Clear bus descriptor
- */
- spc->script = 0;
- spc->error_handler = 0;
- spc->active_target = 0;
- spc->next_target = 0;
- spc->state = 0;
- queue_init(&spc->waiting_targets);
- spc->wd.nactive = 0;
- spc_reset(regs, TRUE);
-
- printf("spc%d: (%d) bus reset ", spc->sc->masterno, ++spc->wd.reset_count);
- delay(scsi_delay_after_reset); /* some targets take long to reset */
-
- if (spc->sc == 0) /* sanity */
- return;
-
- scsi_bus_was_reset(spc->sc);
-}
-
-/*
- * Error handlers
- */
-
-/*
- * Generic, default handler
- */
-boolean_t
-spc_err_generic(spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- register spc_regmap_t *regs = spc->regs;
- LOG(0x10,"err_generic");
-
- if (ints & SPC_INTS_TIMEOUT) /* we timed out */
- if ((regs->spc_scmd & SPC_SCMD_CMDMASK) == SPC_SCMD_C_SELECT)
- {
- /* Powered off ? */
- if (spc->active_target->flags & TGT_FULLY_PROBED)
- {
- spc->active_target->flags = 0;
- LOG(0x1e,"Device Down");
- }
- spc->done = SCSI_RET_DEVICE_DOWN;
- spc_end(spc, ints, psns, ssts);
- return FALSE; /* don't retry - just report missing device */
- }
- else
- { /* timed out - but not on a select. What is going on? */
- gimmeabreak();
- }
-
- if (SPC_CUR_PHASE(psns) == SCSI_PHASE_STATUS)
- return spc_err_to_status(spc, ints, psns, ssts);
- gimmeabreak();
- return FALSE;
-}
-
-/*
- * Handle generic errors that are reported as
- * an unexpected change to STATUS phase
- */
-boolean_t
-spc_err_to_status(spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
- script_t scp = spc->script;
-
- LOG(0x20,"err_tostatus");
- while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS)
- scp++;
- spc->script = scp;
-#if 0
- /*
- * Normally, we would already be able to say the command
- * is in error, e.g. the tape had a filemark or something.
- * But in case we do disconnected mode WRITEs, it is quite
- * common that the following happens:
- * dma_out -> disconnect -> reconnect
- * and our script might expect at this point that the dma
- * had to be restarted (it didn't know it was completed
- * because the tape record is shorter than we asked for).
- * And in any event.. it is both correct and cleaner to
- * declare error iff the STATUS byte says so.
- */
- spc->done = SCSI_RET_NEED_SENSE;
-#endif
- return TRUE;
-}
-
-/*
- * Watch for a disconnection
- */
-boolean_t
-spc_err_disconn(spc, ints, psns, ssts)
- register spc_softc_t spc;
- int ints, psns, ssts;
-{
-#if 1
-/*
- * THIS ROUTINE CAN'T POSSIBLY WORK...
- * FOR EXAMPLE, THE VARIABLE 'xferred' IS NEVER INITIALIZED.
- */
- return FALSE;
-#else
- register spc_regmap_t *regs;
- register target_info_t *tgt;
- int xferred;
-
- LOG(0x18,"err_disconn");
-
- if (SPC_CUR_PHASE(ints) != SCSI_PHASE_MSG_IN)
- return spc_err_generic(spc, ints, psns, ssts);
-
- regs = spc->regs;
-
- tgt = spc->active_target;
-
- switch (SCSI_PHASE(spc->script->condition)) {
- case SCSI_PHASE_DATAO:
- LOG(0x1b,"+DATAO");
-/*updatecounters:*/
- tgt->transient_state.out_count -= xferred;
- assert(tgt->transient_state.out_count > 0);
- tgt->transient_state.dma_offset += xferred;
-
- tgt->transient_state.script = spc_script_data_out;
- break;
-
- case SCSI_PHASE_DATAI:
- LOG(0x19,"+DATAI");
-
-/*update counters: */
- assert(xferred > 0);
- tgt->transient_state.in_count -= xferred;
- assert(tgt->transient_state.in_count > 0);
- tgt->transient_state.dma_offset += xferred;
-
- tgt->transient_state.script = spc_script_data_in;
- break;
-
- case SCSI_PHASE_STATUS:
-
- if (spc->state & SPC_STATE_DMA_IN) {
-
- LOG(0x1a,"+STATUS+R");
-
-/*same as above.. */
- assert(xferred > 0);
- tgt->transient_state.in_count -= xferred;
-/* assert(tgt->transient_state.in_count > 0);*/
- tgt->transient_state.dma_offset += xferred;
-
- tgt->transient_state.script = spc_script_data_in;
- if (tgt->transient_state.in_count == 0)
- tgt->transient_state.script++;
-
- } else {
-
- LOG(0x1d,"+STATUS+W");
-
- if ((tgt->transient_state.out_count == spc->out_count)) {
- /* all done */
- tgt->transient_state.script = &spc_script_data_out[1];
- tgt->transient_state.out_count = 0;
- } else {
-
-/*.. */
- tgt->transient_state.out_count -= xferred;
- assert(tgt->transient_state.out_count > 0);
- tgt->transient_state.dma_offset += xferred;
-
- tgt->transient_state.script = spc_script_data_out;
- }
- spc->out_count = 0;
- }
- break;
- default:
- gimmeabreak();
- }
- /* spc->xxx = 0; */
-
-/* SPC_ACK(regs,SCSI_PHASE_MSG_IN); later */
- (void) spc_msg_in(spc, ints, psns, ssts);
-
- spc->script = spc_script_disconnect;
-
- return FALSE;
-#endif
-}
-
-/*
- * Watchdog
- *
- */
-void spc_reset_scsibus(spc)
- register spc_softc_t spc;
-{
- register target_info_t *tgt = spc->active_target;
- if (tgt) {
- int cnt = 0;
- /* SPC_TC_GET(spc->dmar,cnt); */
- log( LOG_KERN,
- "Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n",
- tgt->target_id, tgt->cur_cmd,
- tgt->transient_state.in_count, tgt->transient_state.out_count,
- spc->in_count, spc->out_count, cnt);
- }
-#if 0
- spc->regs->.....
-#endif
- delay(25);
-}
-
-int SPC_ACK(regs, phase)
-register spc_regmap_t *regs;
-unsigned phase;
-{
- /* we want to switch into the specified phase -
-
- The calling routine should already dismissed
- any pending interrupts (spc_ints)
- */
-
- regs->spc_psns = 0;
- regs->spc_pctl = phase | SPC_PCTL_BFREE_IE;
- return 0;
-}
-#endif /*NSCSI > 0*/
-
-#endif 0