diff options
Diffstat (limited to 'scsi/adapters/scsi_5380_hdw.c')
-rw-r--r-- | scsi/adapters/scsi_5380_hdw.c | 2423 |
1 files changed, 2423 insertions, 0 deletions
diff --git a/scsi/adapters/scsi_5380_hdw.c b/scsi/adapters/scsi_5380_hdw.c new file mode 100644 index 00000000..2fc7d893 --- /dev/null +++ b/scsi/adapters/scsi_5380_hdw.c @@ -0,0 +1,2423 @@ +/* + * 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. + */ +/* + * File: scsi_5380_hdw.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/91 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the NCR 5380 + * 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 <sci.h> +#if NSCI > 0 +#include <platforms.h> + +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> +#include <machine/machspl.h> + +#include <sys/syslog.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#ifdef VAXSTATION +#define PAD(n) char n[3] +#endif + +#include <scsi/adapters/scsi_5380.h> + +#ifdef PAD +typedef struct { + volatile unsigned char sci_data; /* r: Current data */ +/*#define sci_odata sci_data /* w: Out data */ + PAD(pad0); + + volatile unsigned char sci_icmd; /* rw: Initiator command */ + PAD(pad1); + + volatile unsigned char sci_mode; /* rw: Mode */ + PAD(pad2); + + volatile unsigned char sci_tcmd; /* rw: Target command */ + PAD(pad3); + + volatile unsigned char sci_bus_csr; /* r: Bus Status */ +/*#define sci_sel_enb sci_bus_csr /* w: Select enable */ + PAD(pad4); + + volatile unsigned char sci_csr; /* r: Status */ +/*#define sci_dma_send sci_csr /* w: Start dma send data */ + PAD(pad5); + + volatile unsigned char sci_idata; /* r: Input data */ +/*#define sci_trecv sci_idata /* w: Start dma receive, target */ + PAD(pad6); + + volatile unsigned char sci_iack; /* r: Interrupt Acknowledge */ +/*#define sci_irecv sci_iack /* w: Start dma receive, initiator */ + PAD(pad7); + +} sci_padded_regmap_t; +#else +typedef sci_regmap_t sci_padded_regmap_t; +#endif + +#ifdef VAXSTATION +#define check_memory(addr,dow) ((dow) ? wbadaddr(addr,4) : badaddr(addr,4)) + +/* vax3100 */ +#include <chips/vs42x_rb.h> +#define STC_5380_A VAX3100_STC_5380_A +#define STC_5380_B VAX3100_STC_5380_B +#define STC_DMAREG_OFF VAX3100_STC_DMAREG_OFF + +static int mem; /* mem++ seems to take approx 0.34 usecs */ +#define delay_1p2_us() {mem++;mem++;mem++;mem++;} +#define my_scsi_id(ctlr) (ka3100_scsi_id((ctlr))) +#endif /* VAXSTATION */ + + +#ifndef STC_5380_A /* cross compile check */ +typedef struct { + int sci_dma_dir, sci_dma_adr; +} *sci_dmaregs_t; +#define STC_DMAREG_OFF 0 +#define SCI_DMA_DIR_WRITE 0 +#define SCI_DMA_DIR_READ 1 +#define STC_5380_A 0 +#define STC_5380_B 0x100 +#define SCI_RAM_SIZE 0x10000 +#endif + +/* + * The 5380 can't tell you the scsi ID it uses, so + * unless there is another way use the defaults + */ +#ifndef my_scsi_id +#define my_scsi_id(ctlr) (scsi_initiator_id[(ctlr)]) +#endif + +/* + * Statically partition the DMA buffer between targets. + * This way we will eventually be able to attach/detach + * drives on-fly. And 18k/target is enough. + */ +#define PER_TGT_DMA_SIZE ((SCI_RAM_SIZE/7) & ~(sizeof(int)-1)) + +/* + * Round to 4k to make debug easier + */ +#define PER_TGT_BUFF_SIZE ((PER_TGT_DMA_SIZE >> 12) << 12) +#define PER_TGT_BURST_SIZE (PER_TGT_BUFF_SIZE>>1) + +/* + * Macros to make certain things a little more readable + */ + +#define SCI_ACK(ptr,phase) (ptr)->sci_tcmd = (phase) +#define SCI_CLR_INTR(regs) {register int temp = regs->sci_iack;} + + +/* + * 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 sci_intr() for how and where this is all done. + */ + +typedef struct script { + int condition; /* expected state at interrupt */ + int (*action)(); /* action routine */ +} *script_t; + +#define SCRIPT_MATCH(cs,bs) (((bs)&SCI_BUS_BSY)|SCI_CUR_PHASE((bs))) + +#define SCI_PHASE_DISC 0x0 /* sort of .. */ + + +/* forward decls of script actions */ +boolean_t + sci_dosynch(), /* negotiate synch xfer */ + sci_dma_in(), /* get data from target via dma */ + sci_dma_out(), /* send data to target via dma */ + sci_get_status(), /* get status from target */ + sci_end_transaction(), /* all come to an end */ + sci_msg_in(), /* get disconnect message(s) */ + sci_disconnected(); /* current target disconnected */ +/* forward decls of error handlers */ +boolean_t + sci_err_generic(), /* generic error handler */ + sci_err_disconn(), /* when a target disconnects */ + gimmeabreak(); /* drop into the debugger */ + +int sci_reset_scsibus(); +boolean_t sci_probe_target(); + +scsi_ret_t sci_select_target(); + +#ifdef VAXSTATION +/* + * This should be somewhere else, and it was a + * mistake to share this buffer across SCSIs. + */ +struct dmabuffer { + volatile char *base; + char *sbrk; +} dmab[1]; + +volatile char * +sci_buffer_base(unit) +{ + return dmab[unit].base; +} + +sci_buffer_init(dmar, ram) + sci_dmaregs_t dmar; + volatile char *ram; +{ + dmar->sci_dma_rammode = SCI_RAM_EXPMODE; + dmab[0].base = dmab[0].sbrk = (char *) ram; + blkclr((char *) ram, SCI_RAM_SIZE); +} +char * +sci_buffer_sbrk(size) +{ + char *ret = dmab[0].sbrk; + + dmab[0].sbrk += size; + if ((dmab[0].sbrk - dmab[0].base) > SCI_RAM_SIZE) + panic("scialloc"); + return ret; +} + +#endif /* VAXSTATION */ + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) 5380 interface + */ +struct sci_softc { + watchdog_t wd; + sci_padded_regmap_t *regs; /* 5380 registers */ + sci_dmaregs_t dmar; /* DMA controller registers */ + volatile char *buff; /* DMA buffer memory (I/O space) */ + 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 SCI_STATE_BUSY 0x01 /* selecting or currently connected */ +#define SCI_STATE_TARGET 0x04 /* currently selected as target */ +#define SCI_STATE_COLLISION 0x08 /* lost selection attempt */ +#define SCI_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + unsigned char extra_byte; + + 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 */ + +} sci_softc_data[NSCI]; + +typedef struct sci_softc *sci_softc_t; + +sci_softc_t sci_softc[NSCI]; + +/* + * Definition of the controller for the auto-configuration program. + */ + +int sci_probe(), scsi_slave(), sci_go(), sci_intr(); +void scsi_attach(); + +vm_offset_t sci_std[NSCI] = { 0 }; +struct bus_device *sci_dinfo[NSCI*8]; +struct bus_ctlr *sci_minfo[NSCI]; +struct bus_driver sci_driver = + { sci_probe, scsi_slave, scsi_attach, sci_go, sci_std, "rz", sci_dinfo, + "sci", sci_minfo, BUS_INTR_B4_PROBE}; + +/* + * Scripts + */ +struct script +sci_script_data_in[] = { + { SCSI_PHASE_DATAI|SCI_BUS_BSY, sci_dma_in}, + { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status}, + { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction} +}, + +sci_script_data_out[] = { + { SCSI_PHASE_DATAO|SCI_BUS_BSY, sci_dma_out}, + { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status}, + { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction} +}, + +sci_script_cmd[] = { + { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status}, + { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction} +}, + +/* Synchronous transfer neg(oti)ation */ + +sci_script_try_synch[] = { + { SCSI_PHASE_MSG_OUT|SCI_BUS_BSY, sci_dosynch} +}, + +/* Disconnect sequence */ + +sci_script_disconnect[] = { + { SCI_PHASE_DISC, sci_disconnected} +}; + + + +#define u_min(a,b) (((a) < (b)) ? (a) : (b)) + + +#define DEBUG +#ifdef DEBUG + +sci_state(base) + vm_offset_t base; +{ + sci_padded_regmap_t *regs; + sci_dmaregs_t dmar; + extern char *sci; + unsigned dmadr; + int cnt, i; + + if (base == 0) + base = (vm_offset_t)sci; + + for (i = 0; i < 2; i++) { + regs = (sci_padded_regmap_t*) (base + + (i ? STC_5380_B : STC_5380_A)); + dmar = (sci_dmaregs_t) ((char*)regs + STC_DMAREG_OFF); + SCI_DMADR_GET(dmar,dmadr); + SCI_TC_GET(dmar,cnt); + + db_printf("scsi%d: ph %x (sb %x), mode %x, tph %x, csr %x, cmd %x, ", + i, + (unsigned) SCI_CUR_PHASE(regs->sci_bus_csr), + (unsigned) regs->sci_bus_csr, + (unsigned) regs->sci_mode, + (unsigned) regs->sci_tcmd, + (unsigned) regs->sci_csr, + (unsigned) regs->sci_icmd); + db_printf("dma%c %x @ %x\n", + (dmar->sci_dma_dir) ? 'I' : 'O', cnt, dmadr); + } + return 0; +} +sci_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = sci_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(spt,1); + db_printf(": %x ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +sci_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = sci_softc[unit]->sc->target[i]; + if (tgt) + sci_target_state(tgt); + } +} + +sci_script_state(unit) +{ + script_t spt = sci_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym(spt,1); + db_printf(": %x ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(sci_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 sci_logpt; +char sci_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x24 +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG(e,f) + char *f; +{ + sci_log[sci_logpt++] = (e); + if (sci_logpt == LOGSIZE) sci_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +sci_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = sci_logpt; i < LOGSIZE; i++) { + c = sci_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-%d", c & 0x7f); + } + db_printf("\n"); + return 0; +} + +sci_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. + */ +sci_probe(reg, ui) + char *reg; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + sci_softc_t sci = &sci_softc_data[unit]; + int target_id, i; + scsi_softc_t *sc; + register sci_padded_regmap_t *regs; + spl_t s; + boolean_t did_banner = FALSE; + char *cmd_ptr; + static char *here = "sci_probe"; + + /* + * We are only called if the chip is there, + * but make sure anyways.. + */ + regs = (sci_padded_regmap_t *) (reg); + if (check_memory(regs, 0)) + return 0; + +#if notyet + /* Mappable version side */ + SCI_probe(reg, ui); +#endif + + /* + * Initialize hw descriptor + */ + sci_softc[unit] = sci; + sci->regs = regs; + sci->dmar = (sci_dmaregs_t)(reg + STC_DMAREG_OFF); + sci->buff = sci_buffer_base(0); + + queue_init(&sci->waiting_targets); + + sc = scsi_master_alloc(unit, sci); + sci->sc = sc; + + sc->go = sci_go; + sc->probe = sci_probe_target; + sc->watchdog = scsi_watchdog; + sci->wd.reset = sci_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; /* still true */ + + /* + * Reset chip + */ + s = splbio(); + sci_reset(sci, TRUE); + SCI_CLR_INTR(regs); + + /* + * Our SCSI id on the bus. + */ + + sc->initiator_id = my_scsi_id(unit); + 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 = sci_buffer_sbrk(0); + for (target_id = 0; target_id < 8; target_id++) { + + register unsigned csr, dsr; + scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + if (sci_select_target( regs, sc->initiator_id, target_id, FALSE) == SCSI_RET_DEVICE_DOWN) { + SCI_CLR_INTR(regs); + continue; + } + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + /* should be command phase here: we selected wo ATN! */ + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_CMD) + ; + + SCI_ACK(regs,SCSI_PHASE_CMD); + + /* build command in dma area */ + { + 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; + } + + sci_data_out(regs, SCSI_PHASE_CMD, 6, cmd_ptr); + + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_STATUS) + ; + + SCI_ACK(regs,SCSI_PHASE_STATUS); + + sci_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) + scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); + + /* get cmd_complete message */ + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_IN) + ; + + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + + sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &i); + + /* check disconnected, clear all intr bits */ + while (regs->sci_bus_csr & SCI_BUS_BSY) + ; + SCI_ACK(regs,SCI_PHASE_DISC); + + SCI_CLR_INTR(regs); + + /* ... */ + + /* + * Found a target + */ + sci->ntargets++; + { + register target_info_t *tgt; + + tgt = scsi_slave_alloc(unit, target_id, sci); + + /* "virtual" address for our use */ + tgt->cmd_ptr = sci_buffer_sbrk(PER_TGT_DMA_SIZE); + /* "physical" address for dma engine */ + tgt->dma_ptr = (char*)(tgt->cmd_ptr - sci->buff); +#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 +sci_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + sci_softc_t sci = sci_softc[tgt->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + + /* "virtual" address for our use */ + tgt->cmd_ptr = sci_buffer_sbrk(PER_TGT_DMA_SIZE); + /* "physical" address for dma engine */ + tgt->dma_ptr = (char*)(tgt->cmd_ptr - sci->buff); +#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; +} + + +static sci_wait(preg, until) + volatile unsigned char *preg; +{ + int timeo = 1000000; + /* read it over to avoid bus glitches */ + while ( ((*preg & until) != until) || + ((*preg & until) != until) || + ((*preg & until) != until)) { + delay(1); + if (!timeo--) { + printf("sci_wait TIMEO with x%x\n", *preg); + break; + } + } + return *preg; +} + +scsi_ret_t +sci_select_target(regs, myid, id, with_atn) + register sci_padded_regmap_t *regs; + unsigned char myid, id; + boolean_t with_atn; +{ + register unsigned char bid, icmd; + scsi_ret_t ret = SCSI_RET_RETRY; + + if ((regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL))) + return ret; + + /* for our purposes.. */ + myid = 1 << myid; + id = 1 << id; + + regs->sci_sel_enb = myid; /* if not there already */ + + regs->sci_odata = myid; + regs->sci_mode |= SCI_MODE_ARB; + /* AIP might not set if BSY went true after we checked */ + for (bid = 0; bid < 20; bid++) /* 20usec circa */ + if (regs->sci_icmd & SCI_ICMD_AIP) + break; + if ((regs->sci_icmd & SCI_ICMD_AIP) == 0) { + goto lost; + } + + delay(2); /* 2.2us arb delay */ + + if (regs->sci_icmd & SCI_ICMD_LST) { + goto lost; + } + + regs->sci_mode &= ~SCI_MODE_PAR_CHK; + bid = regs->sci_data; + + if ((bid & ~myid) > myid) { + goto lost; + } + if (regs->sci_icmd & SCI_ICMD_LST) { + goto lost; + } + + /* Won arbitration, enter selection phase now */ + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + icmd |= (with_atn ? (SCI_ICMD_SEL|SCI_ICMD_ATN) : SCI_ICMD_SEL); + regs->sci_icmd = icmd; + + if (regs->sci_icmd & SCI_ICMD_LST) { + goto nosel; + } + + /* XXX a target that violates specs might still drive the bus XXX */ + /* XXX should put our id out, and after the delay check nothi XXX */ + /* XXX ng else is out there. XXX */ + + delay_1p2_us(); + + regs->sci_sel_enb = 0; + + regs->sci_odata = myid | id; + + icmd |= SCI_ICMD_BSY|SCI_ICMD_DATA; + regs->sci_icmd = icmd; + + regs->sci_mode &= ~SCI_MODE_ARB; /* 2 deskew delays, too */ + + icmd &= ~SCI_ICMD_BSY; + regs->sci_icmd = icmd; + + /* bus settle delay, 400ns */ + delay(0); /* too much ? */ + + regs->sci_mode |= SCI_MODE_PAR_CHK; + + { + register int timeo = 2500;/* 250 msecs in 100 usecs chunks */ + while ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) + if (--timeo > 0) + delay(100); + else { + goto nodev; + } + } + + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL); + regs->sci_icmd = icmd; +/* regs->sci_sel_enb = myid;*/ /* looks like we should NOT have it */ + return SCSI_RET_SUCCESS; +nodev: + ret = SCSI_RET_DEVICE_DOWN; + regs->sci_sel_enb = myid; +nosel: + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL|SCI_ICMD_ATN); + regs->sci_icmd = icmd; +lost: + bid = regs->sci_mode; + bid &= ~SCI_MODE_ARB; + bid |= SCI_MODE_PAR_CHK; + regs->sci_mode = bid; + + return ret; +} + +sci_data_out(regs, phase, count, data) + register sci_padded_regmap_t *regs; + unsigned char *data; +{ + register unsigned char icmd; + + /* ..checks.. */ + + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); +loop: + if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase) + return count; + + while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0)) + ; + icmd |= SCI_ICMD_DATA; + regs->sci_icmd = icmd; + regs->sci_odata = *data++; + icmd |= SCI_ICMD_ACK; + regs->sci_icmd = icmd; + + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_ACK); + while ( (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ)) + ; + regs->sci_icmd = icmd; + if (--count > 0) + goto loop; + return 0; +} + +sci_data_in(regs, phase, count, data) + register sci_padded_regmap_t *regs; + unsigned char *data; +{ + register unsigned char icmd; + + /* ..checks.. */ + + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); +loop: + if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase) + return count; + + while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0)) + ; + *data++ = regs->sci_data; + icmd |= SCI_ICMD_ACK; + regs->sci_icmd = icmd; + + icmd &= ~SCI_ICMD_ACK; + while ( (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ)) + ; + regs->sci_icmd = icmd; + if (--count > 0) + goto loop; + return 0; + +} + +sci_reset(sci, quickly) + sci_softc_t sci; + boolean_t quickly; +{ + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dma = sci->dmar; + int dummy; + + regs->sci_icmd = SCI_ICMD_TEST; /* don't drive outputs */ + regs->sci_icmd = SCI_ICMD_TEST|SCI_ICMD_RST; + delay(25); + regs->sci_icmd = 0; + + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_PERR_IE; + regs->sci_tcmd = SCI_PHASE_DISC; /* make sure we do not miss transition */ + regs->sci_sel_enb = 0; + + /* idle the dma controller */ + dma->sci_dma_adr = 0; + dma->sci_dma_dir = SCI_DMA_DIR_WRITE; + SCI_TC_PUT(dma,0); + + /* clear interrupt (two might be queued?) */ + SCI_CLR_INTR(regs); + SCI_CLR_INTR(regs); + + if (quickly) + return; + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call sci_bus_reset(). + */ + regs->sci_icmd = SCI_ICMD_RST; + +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +sci_go(tgt, cmd_count, in_count, cmd_only) + target_info_t *tgt; + boolean_t cmd_only; +{ + sci_softc_t sci; + register spl_t s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + + LOG(1,"go"); + + sci = (sci_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; + + if (len > PER_TGT_BUFF_SIZE) + len = PER_TGT_BUFF_SIZE; + bcopy( ior->io_data, + tgt->cmd_ptr + cmd_count, + len); + tgt->transient_state.copy_count = len; + + /* avoid leaks */ + if (len < tgt->block_size) { + bzero( tgt->cmd_ptr + cmd_count + len, + tgt->block_size - len); + 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,tgt->masterno,tgt->target_id); + disconn = disconn && (sci->ntargets > 1); + disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); + + /* + * Setup target state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + handler = (disconn) ? sci_err_disconn : sci_err_generic; + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(0x13,"readop"); + scp = sci_script_data_in; + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x14,"writeop"); + scp = sci_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 = sci_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 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */ + case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */ + scp = sci_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 = sci_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 = sci_script_cmd; + } else { + scp = sci_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 = sci_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 sci structure. + * Note that it is the strategy routine's job + * to serialize ops on the same target as appropriate. + * XXX here and everywhere, locks! + */ + /* + * Protection viz reconnections makes it tricky. + */ +/* s = splbio();*/ + s = splhigh(); + + if (sci->wd.nactive++ == 0) + sci->wd.watchdog_state = SCSI_WD_ACTIVE; + + if (sci->state & SCI_STATE_BUSY) { + /* + * Queue up this target, note that this takes care + * of proper FIFO scheduling of the scsi-bus. + */ + LOG(3,"enqueue"); + enqueue_tail(&sci->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. + */ + sci->state |= SCI_STATE_BUSY; + sci->next_target = tgt; + sci_attempt_selection(sci); + /* + * Note that we might still lose arbitration.. + */ + } + splx(s); +} + +sci_attempt_selection(sci) + sci_softc_t sci; +{ + target_info_t *tgt; + register int out_count; + sci_padded_regmap_t *regs; + sci_dmaregs_t dmar; + register int cmd; + boolean_t ok; + scsi_ret_t ret; + + regs = sci->regs; + dmar = sci->dmar; + tgt = sci->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * Init bus state variables and set registers. + */ + sci->active_target = tgt; + + /* reselection pending ? */ + if ((regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL))) + return; + + sci->script = tgt->transient_state.script; + sci->error_handler = tgt->transient_state.handler; + sci->done = SCSI_RET_IN_PROGRESS; + + sci->in_count = 0; + sci->out_count = 0; + sci->extra_byte = 0; + + /* + * This is a bit involved, but the bottom line is we want to + * know after we selected with or w/o ATN if the selection + * went well (ret) and if it is (ok) to send the command. + */ + ok = TRUE; + if (tgt->flags & TGT_DID_SYNCH) { + if (tgt->transient_state.identify == 0xff) { + /* Select w/o ATN */ + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, FALSE); + } else { + /* Select with ATN */ + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, TRUE); + if (ret == SCSI_RET_SUCCESS) { + register unsigned char icmd; + + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_OUT) + ; + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + icmd &= ~SCI_ICMD_ATN; + regs->sci_icmd = icmd; + SCI_ACK(regs,SCSI_PHASE_MSG_OUT); + ok = (sci_data_out(regs, SCSI_PHASE_MSG_OUT, + 1, &tgt->transient_state.identify) == 0); + } + } + } else if (tgt->flags & TGT_TRY_SYNCH) { + /* Select with ATN, do the synch xfer neg */ + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, TRUE); + if (ret == SCSI_RET_SUCCESS) { + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_OUT) + ; + ok = sci_dosynch( sci, regs->sci_csr, regs->sci_bus_csr); + } + } else { + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, FALSE); + } + + if (ret == SCSI_RET_DEVICE_DOWN) { + sci->done = ret; + sci_end(sci, regs->sci_csr, regs->sci_bus_csr); + return; + } + if ((ret != SCSI_RET_SUCCESS) || !ok) + return; + +/* time this out or do it via dma !! */ + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_CMD) + ; + + /* set dma pointer and counter to xfer command */ + out_count = tgt->transient_state.cmd_count; +#if 0 + SCI_ACK(regs,SCSI_PHASE_CMD); + sci_data_out(regs,SCSI_PHASE_CMD,out_count,tgt->cmd_ptr); + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY; +#else + SCI_DMADR_PUT(dmar,tgt->dma_ptr); + delay_1p2_us(); + SCI_TC_PUT(dmar,out_count); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE; + SCI_ACK(regs,SCSI_PHASE_CMD); + SCI_CLR_INTR(regs); + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY; + regs->sci_icmd = SCI_ICMD_DATA; + regs->sci_dma_send = 1; +#endif +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +sci_intr(unit) +{ + register sci_softc_t sci; + register script_t scp; + register unsigned csr, bs, cmd; + register sci_padded_regmap_t *regs; + boolean_t try_match; +#if notyet + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return SCI_intr(unit); +#endif + + LOG(5,"\n\tintr"); + + sci = sci_softc[unit]; + regs = sci->regs; + + /* ack interrupt */ + csr = regs->sci_csr; + bs = regs->sci_bus_csr; + cmd = regs->sci_icmd; +TR(regs->sci_mode); + SCI_CLR_INTR(regs); + +TR(csr); +TR(bs); +TR(cmd); +TRCHECK; + + if (cmd & SCI_ICMD_RST){ + sci_bus_reset(sci); + return; + } + + /* we got an interrupt allright */ + if (sci->active_target) + sci->wd.watchdog_state = SCSI_WD_ACTIVE; + + /* drop spurious calls */ + if ((csr & SCI_CSR_INT) == 0) { + LOG(2,"SPURIOUS"); + return; + } + + /* Note: reselect has I/O asserted, select has not */ + if ((sci->state & SCI_STATE_TARGET) || + ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == SCI_BUS_SEL)) { + sci_target_intr(sci,csr,bs); + return; + } + + scp = sci->script; + + /* Race: disconnecting, we get the disconnected notification + (csr sez BSY dropped) at the same time a reselect is active */ + if ((csr & SCI_CSR_DISC) && + scp && (scp->condition == SCI_PHASE_DISC)) { + (void) (*scp->action)(sci, csr, bs); + /* takes care of calling reconnect if necessary */ + return; + } + + /* check who got the bus */ + if ((scp == 0) || (cmd & SCI_ICMD_LST) || + ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == (SCI_BUS_SEL|SCI_BUS_IO))) { + sci_reconnect(sci, csr, bs); + return; + } + + if (SCRIPT_MATCH(csr,bs) != scp->condition) { + if (try_match = (*sci->error_handler)(sci, csr, bs)) { + csr = regs->sci_csr; + bs = regs->sci_bus_csr; + } + } else + try_match = TRUE; + + /* might have been side effected */ + scp = sci->script; + + if (try_match && (SCRIPT_MATCH(csr,bs) == scp->condition)) { + /* + * Perform the appropriate operation, + * then proceed + */ + if ((*scp->action)(sci, csr, bs)) { + /* might have been side effected */ + scp = sci->script; + sci->script = scp + 1; + } + } +} + + +sci_target_intr(sci) + register sci_softc_t sci; +{ + panic("SCI: TARGET MODE !!!\n"); +} + +/* + * All the many little things that the interrupt + * routine might switch to + */ +boolean_t +sci_end_transaction( sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs = sci->regs; + char cmc; + + LOG(0x1f,"end_t"); + + /* Stop dma, no interrupt on disconnect */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_MONBSY|SCI_MODE_DMA_IE); +/* dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + + regs->sci_sel_enb = (1 << sci->sc->initiator_id); + + sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &cmc); + + if (cmc != SCSI_COMMAND_COMPLETE) + printf("{T%x}", cmc); + + /* check disconnected, clear all intr bits */ + while (regs->sci_bus_csr & SCI_BUS_BSY) + ; + SCI_CLR_INTR(regs); + SCI_ACK(regs,SCI_PHASE_DISC); + + if (!sci_end(sci, csr, bs)) { + SCI_CLR_INTR(regs); + (void) sci_reconnect(sci, csr, bs); + } + return FALSE; +} + +boolean_t +sci_end( sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + register io_req_t ior; + register sci_padded_regmap_t *regs = sci->regs; + boolean_t reconn_pending; + + LOG(6,"end"); + + tgt = sci->active_target; + + if ((tgt->done = sci->done) == SCSI_RET_IN_PROGRESS) + tgt->done = SCSI_RET_SUCCESS; + + sci->script = 0; + + if (sci->wd.nactive-- == 1) + sci->wd.watchdog_state = SCSI_WD_INACTIVE; + + /* check reconnection not pending */ + bs = regs->sci_bus_csr; + reconn_pending = ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == (SCI_BUS_SEL|SCI_BUS_IO)); + if (!reconn_pending) { + sci_release_bus(sci); + } else { + sci->active_target = 0; +/* sci->state &= ~SCI_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 (reconn_pending) + sci->state &= ~SCI_STATE_BUSY; + } + + return (!reconn_pending); +} + +boolean_t +sci_release_bus(sci) + register sci_softc_t sci; +{ + boolean_t ret = FALSE; + + LOG(9,"release"); + + sci->script = 0; + + if (sci->state & SCI_STATE_COLLISION) { + + LOG(0xB,"collided"); + sci->state &= ~SCI_STATE_COLLISION; + sci_attempt_selection(sci); + + } else if (queue_empty(&sci->waiting_targets)) { + + sci->state &= ~SCI_STATE_BUSY; + sci->active_target = 0; + ret = TRUE; + + } else { + + LOG(0xC,"dequeue"); + sci->next_target = (target_info_t *) + dequeue_head(&sci->waiting_targets); + sci_attempt_selection(sci); + } + return ret; +} + +boolean_t +sci_get_status( sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + scsi2_status_byte_t status; + register target_info_t *tgt; + unsigned int len, mode; + + LOG(0xD,"get_status"); +TRWRAP; + + /* Stop dma */ + regs->sci_icmd = 0; + mode = regs->sci_mode; + regs->sci_mode = (mode & ~(SCI_MODE_DMA|SCI_MODE_DMA_IE)); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + sci->state &= ~SCI_STATE_DMA_IN; + + tgt = sci->active_target; + + if (len = sci->in_count) { + register int count; + SCI_TC_GET(dmar,count); + if ((tgt->cur_cmd != SCSI_CMD_READ) && + (tgt->cur_cmd != SCSI_CMD_LONG_READ)){ + len -= count; + } else { + if (count) { +#if 0 + this is incorrect and besides.. + tgt->ior->io_residual = count; +#endif + len -= count; + } + sci_copyin( tgt, tgt->transient_state.dma_offset, + len, 0, 0); + } + } + + /* to get the phase mismatch intr */ + regs->sci_mode = mode; + + SCI_ACK(regs,SCSI_PHASE_STATUS); + + sci_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits); + + SCI_TC_PUT(dmar,0); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(sci->active_target, SCSI_ERR_STATUS, status.bits, 0); + sci->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + sci->done = SCSI_RET_SUCCESS; + + return TRUE; +} + +boolean_t +sci_dma_in( sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + char *dma_ptr; + register int count; + boolean_t advance_script = TRUE; + + LOG(0xE,"dma_in"); + + /* + * Problem: the 5380 pipelines xfers between the scsibus and + * itself and between itself and the DMA engine --> halting ? + * In the dmaout direction all is ok, except that (see NCR notes) + * the EOP interrupt is generated before the pipe is empty. + * In the dmain direction (here) the interrupt comes when + * one too many bytes have been xferred on chip! + * + * More specifically, IF we asked for count blindly and we had + * more than count bytes coming (double buffering) we would endup + * actually xferring count+1 from the scsibus, but only count + * to memory [hopefully the last byte sits in the sci_datai reg]. + * This could be helped, except most times count is an exact multiple + * of the sector size which is where disks disconnect.... + * + * INSTEAD, we recognize here that we expect more than count bytes + * coming and set the DMA count to count-1 but keep sci->in_count + * above to count. This will be wrong if the target disconnects + * amidst, but we can cure it. + * + * The places where this has an effect are marked by "EXTRA_BYTE" + */ + + tgt = sci->active_target; + sci->state |= SCI_STATE_DMA_IN; + + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + regs->sci_icmd = 0; + + if (sci->in_count == 0) { + /* + * Got nothing yet: either just sent the command + * or just reconnected + */ + register int avail; + + count = tgt->transient_state.in_count; + count = u_min(count, (PER_TGT_BURST_SIZE)); + avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, avail); + + /* common case of 8k-or-less read ? */ + advance_script = (tgt->transient_state.in_count == count); + + } else { + + /* + * We received some data. + */ + register int offset, xferred, eb; + unsigned char extrab = regs->sci_idata; /* EXTRA_BYTE */ + + SCI_TC_GET(dmar,xferred); + assert(xferred == 0); +if (scsi_debug) { +printf("{B %x %x %x (%x)}", + sci->in_count, xferred, sci->extra_byte, extrab); +} + /* ++EXTRA_BYTE */ + xferred = sci->in_count - xferred; + eb = sci->extra_byte; + /* --EXTRA_BYTE */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + count = u_min(tgt->transient_state.in_count, (PER_TGT_BURST_SIZE)); + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) { + tgt->transient_state.dma_offset = 0; + } else { + register int avail; + avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, avail); + } + advance_script = (tgt->transient_state.in_count == count); + + /* get some more */ + dma_ptr = tgt->dma_ptr + tgt->transient_state.dma_offset; + sci->in_count = count; + /* ++EXTRA_BYTE */ + if (!advance_script) { + sci->extra_byte = 1; /* that's the cure.. */ + count--; + } else + sci->extra_byte = 0; + /* --EXTRA_BYTE */ + SCI_TC_PUT(dmar,count); +/* regs->sci_icmd = 0;*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + SCI_ACK(regs,SCSI_PHASE_DATAI); + SCI_CLR_INTR(regs); + regs->sci_mode |= (advance_script ? SCI_MODE_DMA + : (SCI_MODE_DMA|SCI_MODE_DMA_IE)); + dmar->sci_dma_dir = SCI_DMA_DIR_READ; + regs->sci_irecv = 1; + + /* copy what we got */ + sci_copyin( tgt, offset, xferred, eb, extrab); + + /* last chunk ? */ + return advance_script; + } + + sci->in_count = count; + dma_ptr = tgt->dma_ptr + tgt->transient_state.dma_offset; + + /* ++EXTRA_BYTE */ + if (!advance_script) { + sci->extra_byte = 1; /* that's the cure.. */ + count--; + } else + sci->extra_byte = 0; + /* --EXTRA_BYTE */ + + SCI_TC_PUT(dmar,count); +/* regs->sci_icmd = 0;*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + SCI_ACK(regs,SCSI_PHASE_DATAI); + SCI_CLR_INTR(regs); + regs->sci_mode |= (advance_script ? SCI_MODE_DMA + : (SCI_MODE_DMA|SCI_MODE_DMA_IE)); + dmar->sci_dma_dir = SCI_DMA_DIR_READ; + regs->sci_irecv = 1; + + return advance_script; +} + +/* send data to target. Called in three different ways: + (a) to start transfer (b) to restart a bigger-than-8k + transfer (c) after reconnection + */ +int sci_delay = 1; + +boolean_t +sci_dma_out( sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + register char *dma_ptr; + register target_info_t *tgt; + boolean_t advance_script = TRUE; + int count = sci->out_count; + spl_t s; + register int tmp; + + LOG(0xF,"dma_out"); + + tgt = sci->active_target; + sci->state &= ~SCI_STATE_DMA_IN; + + if (sci->out_count == 0) { + /* + * Nothing committed: either just sent the + * command or reconnected + */ + register int remains; + + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/*hold it */ + + regs->sci_icmd = SCI_ICMD_DATA; + + SCI_ACK(regs,SCSI_PHASE_DATAO); + + count = tgt->transient_state.out_count; + count = u_min(count, (PER_TGT_BURST_SIZE)); + remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, remains); + + /* common case of 8k-or-less write ? */ + advance_script = (tgt->transient_state.out_count == count); + } else { + /* + * We sent some data. + * Also, take care of bogus interrupts + */ + register int offset, xferred; + +if (sci_delay & 1) delay(1000); + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/*hold it */ +/* regs->sci_icmd = SCI_ICMD_DATA; */ + + SCI_TC_GET(dmar,xferred); +if (xferred) printf("{A %x}", xferred); + xferred = sci->out_count - xferred; + assert(xferred > 0); + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + count = u_min(tgt->transient_state.out_count, (PER_TGT_BURST_SIZE)); + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) { + tgt->transient_state.dma_offset = 0; + } else { + register int remains; + remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, remains); + } + /* last chunk ? */ + if (tgt->transient_state.out_count == count) + goto quickie; + + /* ship some more */ + dma_ptr = tgt->dma_ptr + + tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; + sci->out_count = count; + /* + * Mistery: sometimes the first byte + * of an 8k chunk is missing from the tape, it must + * be that somehow touching the 5380 registers + * after the dma engine is ready screws up: false DRQ? + */ +s = splhigh(); + SCI_TC_PUT(dmar,count); +/* SCI_CLR_INTR(regs);*/ + regs->sci_mode = SCI_MODE_PAR_CHK | SCI_MODE_DMA | + SCI_MODE_MONBSY | SCI_MODE_DMA_IE; +/* regs->sci_icmd = SCI_ICMD_DATA;*/ + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE; + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + + regs->sci_dma_send = 1; +splx(s); + /* copy some more data */ + sci_copyout(tgt, offset, xferred); + return FALSE; + } + +quickie: + sci->out_count = count; + dma_ptr = tgt->dma_ptr + + tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; + tmp = (advance_script ? + SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY: + SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY|SCI_MODE_DMA_IE); +s = splhigh(); + SCI_TC_PUT(dmar,count); +/* SCI_CLR_INTR(regs);*/ + regs->sci_mode = tmp; +/* regs->sci_icmd = SCI_ICMD_DATA;*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE; + regs->sci_dma_send = 1; +splx(s); + + return advance_script; +} + +/* disconnect-reconnect ops */ + +/* get the message in via dma */ +boolean_t +sci_msg_in(sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + char *dma_ptr; + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + + LOG(0x15,"msg_in"); + + tgt = sci->active_target; + + dma_ptr = tgt->dma_ptr; + /* We would clobber the data for READs */ + if (sci->state & SCI_STATE_DMA_IN) { + register int offset; + offset = tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; + dma_ptr += offset; + } + + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + regs->sci_icmd = 0; + + /* We only really expect two bytes */ + SCI_TC_PUT(dmar,sizeof(scsi_command_group_0)); +/* regs->sci_icmd = 0*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + SCI_CLR_INTR(regs); + regs->sci_mode |= SCI_MODE_DMA; + dmar->sci_dma_dir = SCI_DMA_DIR_READ; + regs->sci_irecv = 1; + + return TRUE; +} + +/* check the message is indeed a DISCONNECT */ +boolean_t +sci_disconnect(sci, csr, bs) + register sci_softc_t sci; +{ + register int len; + boolean_t ok = FALSE; + register sci_dmaregs_t dmar = sci->dmar; + register char *msgs; + unsigned int offset; + + + SCI_TC_GET(dmar,len); + len = sizeof(scsi_command_group_0) - len; + PRINT(("{G%d}",len)); + + /* wherever it was, take it from there */ + SCI_DMADR_GET(dmar,offset); + msgs = (char*)sci->buff + offset - len; + + if ((len == 0) || (len > 2)) + 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]", + sci->active_target->target_id, len, *msgs); + + return TRUE; +} + +/* save all relevant data, free the BUS */ +boolean_t +sci_disconnected(sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + sci_padded_regmap_t *regs = sci->regs; + + regs->sci_mode &= ~(SCI_MODE_MONBSY|SCI_MODE_DMA); + SCI_CLR_INTR(regs);/*retriggered by MONBSY cuz intr routine did CLR */ + SCI_ACK(regs,SCI_PHASE_DISC); + + LOG(0x16,"disconnected"); + + sci_disconnect(sci,csr,bs); + + tgt = sci->active_target; + tgt->flags |= TGT_DISCONNECTED; + tgt->transient_state.handler = sci->error_handler; + /* the rest has been saved in sci_err_disconn() */ + + PRINT(("{D%d}", tgt->target_id)); + + sci_release_bus(sci); + + return FALSE; +} + +/* get reconnect message, restore BUS */ +boolean_t +sci_reconnect(sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + sci_padded_regmap_t *regs; + register int id; + int msg; + + LOG(0x17,"reconnect"); + + if (sci->wd.nactive == 0) { + LOG(2,"SPURIOUS"); + return FALSE; + } + + regs = sci->regs; + + regs->sci_mode &= ~SCI_MODE_PAR_CHK; + id = regs->sci_data;/*parity!*/ + regs->sci_mode |= SCI_MODE_PAR_CHK; + + /* xxx check our id is in there */ + + id &= ~(1 << sci->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->sci_icmd = SCI_ICMD_BSY; + while (regs->sci_bus_csr & SCI_BUS_SEL) + ; + regs->sci_icmd = 0; + delay_1p2_us(); + while ( ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) && + ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) && + ((regs->sci_bus_csr & SCI_BUS_BSY) == 0)) + ; + + regs->sci_mode |= SCI_MODE_MONBSY; + + /* Now should wait for correct phase: REQ signals it */ + while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0)) + ; + + /* + * See if this reconnection collided with a selection attempt + */ + if (sci->state & SCI_STATE_BUSY) + sci->state |= SCI_STATE_COLLISION; + + sci->state |= SCI_STATE_BUSY; + + /* Get identify msg */ + bs = regs->sci_bus_csr; +if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) gimmeabreak(); + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + msg = 0; + sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &msg); + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY; + regs->sci_sel_enb = 0; + + if (msg != SCSI_IDENTIFY) + printf("{I%x %x}", id, msg); + + tgt = sci->sc->target[id]; + if (id > 7 || tgt == 0) panic("sci_reconnect"); + + PRINT(("{R%d}", id)); + if (sci->state & SCI_STATE_COLLISION) + PRINT(("[B %d-%d]", sci->active_target->target_id, id)); + + LOG(0x80+id,0); + + sci->active_target = tgt; + tgt->flags &= ~TGT_DISCONNECTED; + + sci->script = tgt->transient_state.script; + sci->error_handler = tgt->transient_state.handler; + sci->in_count = 0; + sci->out_count = 0; + + /* Should get a phase mismatch when tgt changes phase */ + + return TRUE; +} + + + +/* do the synch negotiation */ +boolean_t +sci_dosynch( sci, csr, bs) + register sci_softc_t sci; +{ + /* + * Phase is MSG_OUT here, cmd has not been xferred + */ + int len; + register target_info_t *tgt; + register sci_padded_regmap_t *regs = sci->regs; + unsigned char off, icmd; + register unsigned char *p; + + regs->sci_mode |= SCI_MODE_MONBSY; + + LOG(0x11,"dosync"); + + /* ATN still asserted */ + SCI_ACK(regs,SCSI_PHASE_MSG_OUT); + + tgt = sci->active_target; + + tgt->flags |= TGT_DID_SYNCH; /* only one chance */ + tgt->flags &= ~TGT_TRY_SYNCH; + + p = (unsigned char *)tgt->cmd_ptr + tgt->transient_state.cmd_count + + tgt->transient_state.dma_offset; + p[0] = SCSI_IDENTIFY; + p[1] = SCSI_EXTENDED_MESSAGE; + p[2] = 3; + p[3] = SCSI_SYNC_XFER_REQUEST; + /* We cannot run synchronous */ +#define sci_to_scsi_period(x) 0xff +#define scsi_period_to_sci(x) (x) + off = 0; + p[4] = sci_to_scsi_period(sci_min_period); + p[5] = off; + + /* xfer all but last byte with ATN set */ + sci_data_out(regs, SCSI_PHASE_MSG_OUT, + sizeof(scsi_synch_xfer_req_t), p); + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + icmd &= ~SCI_ICMD_ATN; + regs->sci_icmd = icmd; + sci_data_out(regs, SCSI_PHASE_MSG_OUT, 1, + &p[sizeof(scsi_synch_xfer_req_t)]); + + /* wait for phase change */ + while (regs->sci_csr & SCI_CSR_PHASE_MATCH) + ; + bs = regs->sci_bus_csr; + + /* The standard sez there nothing else the target can do but.. */ + if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) + panic("sci_dosync");/* XXX put offline */ + +msgin: + /* ack */ + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + + /* get answer */ + len = sizeof(scsi_synch_xfer_req_t); + len = sci_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_sci(p[3]); + tgt->sync_offset = p[4]; + /* sanity */ + if (tgt->sync_offset != 0) + printf(" ?OFFSET %x? ", tgt->sync_offset); + } + + /* wait for phase change */ + while (regs->sci_csr & SCI_CSR_PHASE_MATCH) + ; + bs = regs->sci_bus_csr; + + /* phase should be command now */ + /* continue with simple command script */ + sci->error_handler = sci_err_generic; + sci->script = sci_script_cmd; + + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_CMD ) + return TRUE; + +/* sci->script++;*/ + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_STATUS ) + return TRUE; /* intr is pending */ + + sci->script++; + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_MSG_IN ) + return TRUE; + + if ((bs & SCI_BUS_BSY) == 0) /* uhu? disconnected */ + return sci_end_transaction(sci, regs->sci_csr, regs->sci_bus_csr); + + panic("sci_dosynch"); + return FALSE; +} + +/* + * The bus was reset + */ +sci_bus_reset(sci) + register sci_softc_t sci; +{ + register target_info_t *tgt; + register sci_padded_regmap_t *regs = sci->regs; + int i; + + LOG(0x21,"bus_reset"); + + /* + * Clear bus descriptor + */ + sci->script = 0; + sci->error_handler = 0; + sci->active_target = 0; + sci->next_target = 0; + sci->state = 0; + queue_init(&sci->waiting_targets); + sci->wd.nactive = 0; + sci_reset(sci, TRUE); + + printf("sci%d: (%d) bus reset ", sci->sc->masterno, ++sci->wd.reset_count); + delay(scsi_delay_after_reset); /* some targets take long to reset */ + + if (sci->sc == 0) /* sanity */ + return; + + scsi_bus_was_reset(sci->sc); +} + +/* + * Error handlers + */ + +/* + * Generic, default handler + */ +boolean_t +sci_err_generic(sci, csr, bs) + register sci_softc_t sci; +{ + register int cond = sci->script->condition; + + LOG(0x10,"err_generic"); + + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_STATUS) + return sci_err_to_status(sci, csr, bs); + gimmeabreak(); + return FALSE; +} + +/* + * Handle generic errors that are reported as + * an unexpected change to STATUS phase + */ +sci_err_to_status(sci, csr, bs) + register sci_softc_t sci; +{ + script_t scp = sci->script; + + LOG(0x20,"err_tostatus"); + while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS) + scp++; + sci->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. + */ + sci->done = SCSI_RET_NEED_SENSE; +#endif + return TRUE; +} + +/* + * Watch for a disconnection + */ +boolean_t +sci_err_disconn(sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs; + register sci_dmaregs_t dmar = sci->dmar; + register target_info_t *tgt; + int count; + + LOG(0x18,"err_disconn"); + + if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) + return sci_err_generic(sci, csr, bs); + + regs = sci->regs; + + tgt = sci->active_target; + + switch (SCSI_PHASE(sci->script->condition)) { + case SCSI_PHASE_DATAO: + LOG(0x1b,"+DATAO"); + +if (sci_delay & 1) delay(1000); + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/* make sure we steal not */ + + if (sci->out_count) { + register int xferred, offset; + + SCI_TC_GET(dmar,xferred); +if (scsi_debug) +printf("{O %x %x}", xferred, sci->out_count); + /* 5380 prefetches */ + xferred = sci->out_count - xferred - 1; +/* assert(xferred > 0);*/ + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + sci_copyout( tgt, offset, xferred); + + } + tgt->transient_state.script = sci_script_data_out; + break; + + case SCSI_PHASE_DATAI: + LOG(0x19,"+DATAI"); + + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + if (sci->in_count) { + register int offset, xferred; +/* unsigned char extrab = regs->sci_idata;*/ + + SCI_TC_GET(dmar,xferred); + /* ++EXTRA_BYTE */ +if (scsi_debug) +printf("{A %x %x %x}", xferred, sci->in_count, sci->extra_byte); + xferred = sci->in_count - xferred - sci->extra_byte; + /* ++EXTRA_BYTE */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + /* copy what we got */ + sci_copyin( tgt, offset, xferred, 0, 0/*extrab*/); + } + tgt->transient_state.script = sci_script_data_in; + break; + + case SCSI_PHASE_STATUS: + /* will have to restart dma */ + SCI_TC_GET(dmar,count); + if (sci->state & SCI_STATE_DMA_IN) { + register int offset, xferred; +/* unsigned char extrab = regs->sci_idata;*/ + + LOG(0x1a,"+STATUS+R"); + + + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + /* ++EXTRA_BYTE */ +if (scsi_debug) +printf("{A %x %x %x}", count, sci->in_count, sci->extra_byte); + xferred = sci->in_count - count - sci->extra_byte; + /* ++EXTRA_BYTE */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; +/* assert(tgt->transient_state.in_count > 0);*/ + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + /* copy what we got */ + sci_copyin( tgt, offset, xferred, 0, 0/*/extrab*/); + + tgt->transient_state.script = sci_script_data_in; + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } else { + + LOG(0x1d,"+STATUS+W"); + +if (sci_delay & 1) delay(1000); + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/* make sure we steal not */ + +if (scsi_debug) +printf("{O %x %x}", count, sci->out_count); + if ((count == 0) && (tgt->transient_state.out_count == sci->out_count)) { + /* all done */ + tgt->transient_state.script = &sci_script_data_out[1]; + tgt->transient_state.out_count = 0; + } else { + register int xferred, offset; + + /* how much we xferred */ + xferred = sci->out_count - count - 1;/*prefetch*/ + + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + sci_copyout( tgt, offset, xferred); + + tgt->transient_state.script = sci_script_data_out; + } + sci->out_count = 0; + } + break; + default: + gimmeabreak(); + } + sci->extra_byte = 0; + +/* SCI_ACK(regs,SCSI_PHASE_MSG_IN); later */ + (void) sci_msg_in(sci,csr,bs); + + regs->sci_sel_enb = (1 << sci->sc->initiator_id); + + sci->script = sci_script_disconnect; + + return FALSE; +} + +/* + * Watchdog + * + */ +sci_reset_scsibus(sci) + register sci_softc_t sci; +{ + register target_info_t *tgt = sci->active_target; + if (tgt) { + int cnt; + SCI_TC_GET(sci->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, + sci->in_count, sci->out_count, cnt); + } + sci->regs->sci_icmd = SCI_ICMD_RST; + delay(25); +} + +/* + * Copy routines + */ +/*static*/ +sci_copyin(tgt, offset, len, isaobb, obb) + register target_info_t *tgt; + unsigned char obb; +{ + register char *from, *to; + register int count; + + count = tgt->transient_state.copy_count; + + from = tgt->cmd_ptr + offset; + to = tgt->ior->io_data + count; + tgt->transient_state.copy_count = count + len; + + bcopy( from, to, len); + /* check for last, poor little odd byte */ + if (isaobb) { + to += len; + to[-1] = obb; + } +} + +/*static*/ +sci_copyout( tgt, offset, len) + register target_info_t *tgt; +{ + register char *from, *to; + register int count, olen; + unsigned char c; + char *p; + + count = tgt->ior->io_count - tgt->transient_state.copy_count; + if (count > 0) { + + len = u_min(count, len); + offset += tgt->transient_state.cmd_count; + + count = tgt->transient_state.copy_count; + tgt->transient_state.copy_count = count + len; + + from = tgt->ior->io_data + count; + to = tgt->cmd_ptr + offset; + + bcopy(from, to, len); + + } +} + +#endif /*NSCI > 0*/ + |