diff options
Diffstat (limited to 'scsi/adapters/scsi_53C94_hdw.c')
-rw-r--r-- | scsi/adapters/scsi_53C94_hdw.c | 2840 |
1 files changed, 0 insertions, 2840 deletions
diff --git a/scsi/adapters/scsi_53C94_hdw.c b/scsi/adapters/scsi_53C94_hdw.c deleted file mode 100644 index dad9b223..00000000 --- a/scsi/adapters/scsi_53C94_hdw.c +++ /dev/null @@ -1,2840 +0,0 @@ -/* - * Mach Operating System - * Copyright (c) 1993,1992,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 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_53C94_hdw.h - * Author: Alessandro Forin, Carnegie Mellon University - * Date: 9/90 - * - * Bottom layer of the SCSI driver: chip-dependent functions - * - * This file contains the code that is specific to the NCR 53C94 - * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start - * operation, and interrupt routine. - */ - -/* - * This layer works based on small simple 'scripts' that are installed - * at the start of the command and drive the chip to completion. - * The idea comes from the specs of the NCR 53C700 'script' processor. - * - * There are various reasons for this, mainly - * - Performance: identify the common (successful) path, and follow it; - * at interrupt time no code is needed to find the current status - * - Code size: it should be easy to compact common operations - * - Adaptability: the code skeleton should adapt to different chips without - * terrible complications. - * - Error handling: and it is easy to modify the actions performed - * by the scripts to cope with strange but well identified sequences - * - */ - -#include <asc.h> -#if NASC > 0 -#include <platforms.h> - -#ifdef DECSTATION -typedef unsigned char asc_register_t; -#define PAD(n) char n[3]; -#define mb() -#ifdef MACH_KERNEL -#define HAS_MAPPED_SCSI -#endif -#define ASC_PROBE_DYNAMICALLY FALSE /* established custom */ -#define DEBUG 1 -#define TRACE 1 -#endif - -#ifdef FLAMINGO -typedef unsigned int asc_register_t; -#define PAD(n) int n; /* sparse ! */ -#define mb() wbflush() /* memory barrier */ -#define ASC_PROBE_DYNAMICALLY TRUE -#define DEBUG 1 -#define TRACE 1 -#endif - -#include <mach/std_types.h> -#include <sys/types.h> -#include <chips/busses.h> -#include <scsi/compat_30.h> -#include <machine/machspl.h> - -#include <scsi/scsi.h> -#include <scsi/scsi2.h> - -#include <scsi/adapters/scsi_53C94.h> -#include <scsi/scsi_defs.h> -#include <scsi/adapters/scsi_dma.h> - -#define private static - -#ifdef PAD -typedef struct { - volatile asc_register_t asc_tc_lsb; /* rw: Transfer Counter LSB */ - PAD(pad0) - volatile asc_register_t asc_tc_msb; /* rw: Transfer Counter MSB */ - PAD(pad1) - volatile asc_register_t asc_fifo; /* rw: FIFO top */ - PAD(pad2) - volatile asc_register_t asc_cmd; /* rw: Command */ - PAD(pad3) - volatile asc_register_t asc_csr; /* r: Status */ -/*#define asc_dbus_id asc_csr /* w: Destination Bus ID */ - PAD(pad4) - volatile asc_register_t asc_intr; /* r: Interrupt */ -/*#define asc_sel_timo asc_intr /* w: (re)select timeout */ - PAD(pad5) - volatile asc_register_t asc_ss; /* r: Sequence Step */ -/*#define asc_syn_p asc_ss /* w: synchronous period */ - PAD(pad6) - volatile asc_register_t asc_flags; /* r: FIFO flags + seq step */ -/*#define asc_syn_o asc_flags /* w: synchronous offset */ - PAD(pad7) - volatile asc_register_t asc_cnfg1; /* rw: Configuration 1 */ - PAD(pad8) - volatile asc_register_t asc_ccf; /* w: Clock Conv. Factor */ - PAD(pad9) - volatile asc_register_t asc_test; /* w: Test Mode */ - PAD(pad10) - volatile asc_register_t asc_cnfg2; /* rw: Configuration 2 */ - PAD(pad11) - volatile asc_register_t asc_cnfg3; /* rw: Configuration 3 */ - PAD(pad12) - volatile asc_register_t asc_rfb; /* w: Reserve FIFO byte */ - PAD(pad13) -} asc_padded_regmap_t; - -#else /* !PAD */ - -typedef asc_regmap_t asc_padded_regmap_t; - -#endif /* !PAD */ - -#define get_reg(r,x) ((unsigned char)((r)->x)) - -#define fifo_count(r) ((r)->asc_flags & ASC_FLAGS_FIFO_CNT) -#define get_fifo(r) get_reg(r,asc_fifo) - -boolean_t asc_probe_dynamically = ASC_PROBE_DYNAMICALLY; - -/* - * We might need to use some fields usually - * handled by the DMA engine, if asked to. - * These are "dma_ptr" and "hba_dep". - */ -#define has_oddb hba_dep[0] -#define the_oddb hba_dep[1] - -/* - * A script has a three parts: a pre-condition, an action, and - * an optional command to the chip. The first triggers error - * handling if not satisfied and in our case it is a match - * of the expected and actual scsi-bus phases. - * The action part is just a function pointer, and the - * command is what the 53c90 should be told to do at the end - * of the action processing. This command is only issued and the - * script proceeds if the action routine returns TRUE. - * See asc_intr() for how and where this is all done. - */ - -typedef struct script { - unsigned char condition; /* expected state at interrupt */ - unsigned char command; /* command to the chip */ - unsigned short flags; /* unused padding */ - boolean_t (*action)(); /* extra operations */ -} *script_t; - -/* Matching on the condition value */ -#define ANY 0xff -#define SCRIPT_MATCH(csr,ir,value) ((SCSI_PHASE(csr)==(value)) || \ - (((value)==ANY) && \ - ((ir)&(ASC_INT_DISC|ASC_INT_FC)))) - -/* When no command is needed */ -#define SCRIPT_END -1 - -/* forward decls of script actions */ -boolean_t - asc_end(), /* all come to an end */ - asc_clean_fifo(), /* .. in preparation for status byte */ - asc_get_status(), /* get status from target */ - asc_put_status(), /* send status to initiator */ - asc_dma_in(), /* get data from target via dma */ - asc_dma_in_r(), /* get data from target via dma (restartable)*/ - asc_dma_out(), /* send data to target via dma */ - asc_dma_out_r(), /* send data to target via dma (restartable) */ - asc_dosynch(), /* negotiate synch xfer */ - asc_msg_in(), /* receive the disconenct message */ - asc_disconnected(), /* target has disconnected */ - asc_reconnect(); /* target reconnected */ - -/* forward decls of error handlers */ -boolean_t - asc_err_generic(), /* generic handler */ - asc_err_disconn(), /* target disconnects amidst */ - gimmeabreak(); /* drop into the debugger */ - -int asc_reset_scsibus(); -boolean_t asc_probe_target(); -private asc_wait(); - -/* - * State descriptor for this layer. There is one such structure - * per (enabled) SCSI-53c90 interface - */ -struct asc_softc { - watchdog_t wd; - asc_padded_regmap_t *regs; /* 53c90 registers */ - - scsi_dma_ops_t *dma_ops; /* DMA operations and state */ - opaque_t dma_state; - - script_t script; /* what should happen next */ - boolean_t (*error_handler)();/* what if something is wrong */ - int in_count; /* amnt we expect to receive */ - int out_count; /* amnt we are going to ship */ - - volatile char state; -#define ASC_STATE_BUSY 0x01 /* selecting or currently connected */ -#define ASC_STATE_TARGET 0x04 /* currently selected as target */ -#define ASC_STATE_COLLISION 0x08 /* lost selection attempt */ -#define ASC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ -#define ASC_STATE_SPEC_DMA 0x20 /* special, 8 byte threshold dma */ -#define ASC_STATE_DO_RFB 0x40 /* DMA engine cannot handle odd bytes */ - - unsigned char ntargets; /* how many alive on this scsibus */ - unsigned char done; - unsigned char extra_count; /* sleazy trick to spare an interrupt */ - int dmacnt_at_end; - - scsi_softc_t *sc; /* HBA-indep info */ - target_info_t *active_target; /* the current one */ - - target_info_t *next_target; /* trying to seize bus */ - queue_head_t waiting_targets;/* other targets competing for bus */ - - unsigned char ss_was; /* districate powered on/off devices */ - unsigned char cmd_was; - - unsigned char timeout; /* cache a couple numbers */ - unsigned char ccf; - unsigned char clk; - -} asc_softc_data[NASC]; - -typedef struct asc_softc *asc_softc_t; - -asc_softc_t asc_softc[NASC]; - -/* - * Synch xfer parameters, and timing conversions - */ -int asc_min_period = 5; /* in CLKS/BYTE, e.g. 1 CLK = 40nsecs @25 Mhz */ -int asc_max_offset = 15; /* pure number */ - -int asc_to_scsi_period(a,clk) -{ - /* Note: the SCSI unit is 4ns, hence - A_P * 1,000,000,000 - ------------------- = S_P - C_Mhz * 4 - */ - return a * (250 / clk); - -} - -int scsi_period_to_asc(p,clk) -{ - register int ret; - - ret = (p * clk) / 250; - if (ret < asc_min_period) - return asc_min_period; - if ((asc_to_scsi_period(ret,clk)) < p) - return ret + 1; - return ret; -} - -#define readback(a) {register int foo; foo = a; mb();} - -#define u_min(a,b) (((a) < (b)) ? (a) : (b)) - -/* - * Definition of the controller for the auto-configuration program. - */ - -int asc_probe(), scsi_slave(), asc_go(), asc_intr(); -void scsi_attach(); - -vm_offset_t asc_std[NASC] = { 0 }; -struct bus_device *asc_dinfo[NASC*8]; -struct bus_ctlr *asc_minfo[NASC]; -struct bus_driver asc_driver = - { asc_probe, scsi_slave, scsi_attach, asc_go, asc_std, "rz", asc_dinfo, - "asc", asc_minfo, BUS_INTR_B4_PROBE}; - - -int asc_clock_speed_in_mhz[NASC] = {25,25,25,25}; /* original 3max */ - -asc_set_dmaops(unit, dmaops) - unsigned int unit; - scsi_dma_ops_t *dmaops; -{ - if (unit < NASC) - asc_std[unit] = (vm_offset_t)dmaops; -} - -/* - * Scripts - */ -struct script -asc_script_data_in[] = { /* started with SEL & DMA */ - {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in}, - {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, - -asc_script_data_out[] = { /* started with SEL & DMA */ - {SCSI_PHASE_DATAO, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_out}, - {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, - -asc_script_try_synch[] = { - {SCSI_PHASE_MSG_OUT, ASC_CMD_I_COMPLETE,0, asc_dosynch}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, - -asc_script_simple_cmd[] = { - {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, - -asc_script_disconnect[] = { - {ANY, ASC_CMD_ENABLE_SEL, 0, asc_disconnected}, -/**/ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_reconnect} -}, - -asc_script_restart_data_in[] = { /* starts after disconnect */ - {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in_r}, - {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, - -asc_script_restart_data_out[] = { /* starts after disconnect */ - {SCSI_PHASE_DATAO, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_out_r}, - {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, - -#if documentation -/* - * This is what might happen during a read - * that disconnects - */ -asc_script_data_in_wd[] = { /* started with SEL & DMA & allow disconnect */ - {SCSI_PHASE_MSG_IN, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_msg_in}, - {ANY, ASC_CMD_ENABLE_SEL, 0, asc_disconnected}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_reconnect}, - {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in}, - {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, - {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, -#endif - -/* - * Target mode scripts - */ -asc_script_t_data_in[] = { - {SCSI_PHASE_CMD, ASC_CMD_RCV_DATA|ASC_CMD_DMA, 0, asc_dma_in_r}, - {SCSI_PHASE_DATAO, ASC_CMD_TERM, 0, asc_put_status}, - {ANY, SCRIPT_END, 0, asc_end} -}, - -asc_script_t_data_out[] = { - {SCSI_PHASE_CMD, ASC_CMD_SND_DATA|ASC_CMD_DMA, 0, asc_dma_out_r}, - {SCSI_PHASE_DATAI, ASC_CMD_TERM, 0, asc_put_status}, - {ANY, SCRIPT_END, 0, asc_end} -}; - - -#ifdef DEBUG - -#define PRINT(x) if (scsi_debug) printf x - -asc_state(regs) - asc_padded_regmap_t *regs; -{ - register unsigned char ff,csr,ir,d0,d1,cmd; - - if (regs == 0) { - if (asc_softc[0]) - regs = asc_softc[0]->regs; - else - regs = (asc_padded_regmap_t*)0xbf400000; - } - ff = get_reg(regs,asc_flags); - csr = get_reg(regs,asc_csr); -/* ir = get_reg(regs,asc_intr); nope, clears interrupt */ - d0 = get_reg(regs,asc_tc_lsb); - d1 = get_reg(regs,asc_tc_msb); - cmd = get_reg(regs,asc_cmd); - printf("dma %x ff %x csr %x cmd %x\n", - (d1 << 8) | d0, ff, csr, cmd); - return 0; -} - -asc_target_state(tgt) - target_info_t *tgt; -{ - if (tgt == 0) - tgt = asc_softc[0]->active_target; - if (tgt == 0) - return 0; - db_printf("@x%x: fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n", - tgt, - tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd, - tgt->cmd_ptr, (long)tgt->target_id, - (long)tgt->sync_period, (long)tgt->sync_offset, - tgt->ior, (long)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 %x ", spt->condition, spt->command); - db_printsym(spt->action,1); - db_printf(", "); - db_printsym(tgt->transient_state.handler, 1); - db_printf("\n"); - } - - return 0; -} - -asc_all_targets(unit) -{ - int i; - target_info_t *tgt; - for (i = 0; i < 8; i++) { - tgt = asc_softc[unit]->sc->target[i]; - if (tgt) - asc_target_state(tgt); - } -} - -asc_script_state(unit) -{ - script_t spt = asc_softc[unit]->script; - - if (spt == 0) return 0; - db_printsym(spt,1); - db_printf(": %x %x ", spt->condition, spt->command); - db_printsym(spt->action,1); - db_printf(", "); - db_printsym(asc_softc[unit]->error_handler, 1); - return 0; -} - -#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} - - -#ifdef TRACE - -#define LOGSIZE 256 -int asc_logpt; -char asc_log[LOGSIZE]; - -#define MAXLOG_VALUE 0x42 -struct { - char *name; - unsigned int count; -} logtbl[MAXLOG_VALUE]; - -/* private */ LOG(e,f) - char *f; -{ - asc_log[asc_logpt++] = (e); - if (asc_logpt == LOGSIZE) asc_logpt = 0; - if ((e) < MAXLOG_VALUE) { - logtbl[(e)].name = (f); - logtbl[(e)].count++; - } -} - -asc_print_log(skip) - int skip; -{ - register int i, j; - register unsigned char c; - - for (i = 0, j = asc_logpt; i < LOGSIZE; i++) { - c = asc_log[j]; - if (++j == LOGSIZE) j = 0; - if (skip-- > 0) - continue; - if (c < MAXLOG_VALUE) - db_printf(" %s", logtbl[c].name); - else - db_printf("-x%x", c & 0x7f); - } -} - -asc_print_stat() -{ - register int i; - register char *p; - for (i = 0; i < MAXLOG_VALUE; i++) { - if (p = logtbl[i].name) - printf("%d %s\n", logtbl[i].count, p); - } -} - -#else /*TRACE*/ -#define LOG(e,f) -#define LOGSIZE -#endif /*TRACE*/ - -#else /*DEBUG*/ -#define PRINT(x) -#define LOG(e,f) -#define LOGSIZE -#define 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 a test-unit-ready to each possible target on the bus - * except of course ourselves. - */ -asc_probe(reg, ui) - vm_offset_t reg; - struct bus_ctlr *ui; -{ - int unit = ui->unit; - asc_softc_t asc = &asc_softc_data[unit]; - int target_id; - scsi_softc_t *sc; - register asc_padded_regmap_t *regs; - spl_t s; - boolean_t did_banner = FALSE; - - /* - * We are only called if the right board is there, - * but make sure anyways.. - */ - if (check_memory(reg, 0)) - return 0; - -#if defined(HAS_MAPPED_SCSI) - /* Mappable version side */ - ASC_probe(reg, ui); -#endif - - /* - * Initialize hw descriptor, cache some pointers - */ - asc_softc[unit] = asc; - asc->regs = (asc_padded_regmap_t *) (reg); - - if ((asc->dma_ops = (scsi_dma_ops_t *)asc_std[unit]) == 0) - /* use same as unit 0 if undefined */ - asc->dma_ops = (scsi_dma_ops_t *)asc_std[0]; - { - int dma_bsize = 16; /* bits, preferred */ - boolean_t do_rfb = FALSE; - - asc->dma_state = (*asc->dma_ops->init)(unit, reg, &dma_bsize, &do_rfb); - if (dma_bsize > 16) - asc->state |= ASC_STATE_SPEC_DMA; - if (do_rfb) - asc->state |= ASC_STATE_DO_RFB; - } - - queue_init(&asc->waiting_targets); - - asc->clk = asc_clock_speed_in_mhz[unit]; - asc->ccf = mhz_to_ccf(asc->clk); /* see .h file */ - asc->timeout = asc_timeout_250(asc->clk,asc->ccf); - - sc = scsi_master_alloc(unit, asc); - asc->sc = sc; - - sc->go = asc_go; - sc->watchdog = scsi_watchdog; - sc->probe = asc_probe_target; - asc->wd.reset = asc_reset_scsibus; - -#ifdef MACH_KERNEL - sc->max_dma_data = -1; -#else - sc->max_dma_data = scsi_per_target_virtual; -#endif - - regs = asc->regs; - - /* - * Our SCSI id on the bus. - * The user can set this via the prom on 3maxen/pmaxen. - * If this changes it is easy to fix: make a default that - * can be changed as boot arg. - */ - { - register unsigned char my_id; - - my_id = scsi_initiator_id[unit] & 0x7; - if (my_id != 7) - regs->asc_cnfg1 = my_id; mb(); - } - - /* - * Reset chip, fully. Note that interrupts are already enabled. - */ - s = splbio(); - asc_reset(asc, TRUE, asc->state & ASC_STATE_SPEC_DMA); - - sc->initiator_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; - printf("%s%d: SCSI id %d", ui->name, unit, sc->initiator_id); - - { - register target_info_t *tgt; - - tgt = scsi_slave_alloc(sc->masterno, sc->initiator_id, asc); - (*asc->dma_ops->new_target)(asc->dma_state, tgt); - sccpu_new_initiator(tgt, tgt); - } - - if (asc_probe_dynamically) - printf("%s", ", will probe targets on demand"); - else { - - /* - * For all possible targets, see if there is one and allocate - * a descriptor for it if it is there. - */ - for (target_id = 0; target_id < 8; target_id++) { - register unsigned char csr, ss, ir, ff; - register scsi_status_byte_t status; - - /* except of course ourselves */ - if (target_id == sc->initiator_id) - continue; - - regs->asc_cmd = ASC_CMD_FLUSH; /* empty fifo */ - mb(); - delay(2); - - regs->asc_dbus_id = target_id; mb(); - regs->asc_sel_timo = asc->timeout; mb(); - - /* - * See if the unit is ready. - * XXX SHOULD inquiry LUN 0 instead !!! - */ - regs->asc_fifo = SCSI_CMD_TEST_UNIT_READY; mb(); - regs->asc_fifo = 0; mb(); - regs->asc_fifo = 0; mb(); - regs->asc_fifo = 0; mb(); - regs->asc_fifo = 0; mb(); - regs->asc_fifo = 0; mb(); - - /* select and send it */ - regs->asc_cmd = ASC_CMD_SEL; mb(); - - /* wait for the chip to complete, or timeout */ - csr = asc_wait(regs, ASC_CSR_INT, 1); - ss = get_reg(regs,asc_ss); - ir = get_reg(regs,asc_intr); - - /* empty fifo, there is garbage in it if timeout */ - regs->asc_cmd = ASC_CMD_FLUSH; mb(); - delay(2); - - /* - * Check if the select timed out - */ - if ((ASC_SS(ss) == 0) && (ir == ASC_INT_DISC)) - /* noone out there */ - continue; - - if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) { - printf( " %s%d%s", "ignoring target at ", target_id, - " cuz it acts weirdo"); - continue; - } - - printf(",%s%d", did_banner++ ? " " : " target(s) at ", - target_id); - - regs->asc_cmd = ASC_CMD_I_COMPLETE; - wbflush(); - csr = asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr); /* ack intr */ - mb(); - - status.bits = get_fifo(regs); /* empty fifo */ - mb(); - ff = get_fifo(regs); - - if (status.st.scsi_status_code != SCSI_ST_GOOD) - scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); - - regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); - csr = asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr); /* ack intr */ - mb(); - - /* - * Found a target - */ - asc->ntargets++; - { - register target_info_t *tgt; - tgt = scsi_slave_alloc(sc->masterno, target_id, asc); - - (*asc->dma_ops->new_target)(asc->dma_state, tgt); - } - } - } /* asc_probe_dynamically */ - - regs->asc_cmd = ASC_CMD_ENABLE_SEL; mb(); - - printf(".\n"); - - splx(s); - return 1; -} - -boolean_t -asc_probe_target(tgt, ior) - target_info_t *tgt; - io_req_t ior; -{ - asc_softc_t asc = asc_softc[tgt->masterno]; - boolean_t newlywed; - - newlywed = (tgt->cmd_ptr == 0); - if (newlywed) { - (*asc->dma_ops->new_target)(asc->dma_state, tgt); - } - - if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) - return FALSE; - - asc->ntargets++; - tgt->flags = TGT_ALIVE; - return TRUE; -} - -private asc_wait(regs, until, complain) - asc_padded_regmap_t *regs; -{ - int timeo = 1000000; - while ((regs->asc_csr & until) == 0) { - mb(); - delay(1); - if (!timeo--) { - if (complain) - printf("asc_wait TIMEO with x%x\n", get_reg(regs,asc_csr)); - break; - } - } - return get_reg(regs,asc_csr); -} - -asc_reset(asc, quick, special_dma) - asc_softc_t asc; -{ - char my_id; - int ccf; - asc_padded_regmap_t *regs; - - regs = asc->regs; - - /* preserve our ID for now */ - my_id = (regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID); - - /* - * Reset chip and wait till done - */ - regs->asc_cmd = ASC_CMD_RESET; - wbflush(); delay(25); - - /* spec says this is needed after reset */ - regs->asc_cmd = ASC_CMD_NOP; - wbflush(); delay(25); - - /* - * Set up various chip parameters - */ - regs->asc_ccf = asc->ccf; - wbflush(); - delay(25); - regs->asc_sel_timo = asc->timeout; mb(); - /* restore our ID */ - regs->asc_cnfg1 = my_id | ASC_CNFG1_P_CHECK; mb(); - regs->asc_cnfg2 = ASC_CNFG2_SCSI2; - mb(); - regs->asc_cnfg3 = special_dma ? (ASC_CNFG3_T8|ASC_CNFG3_ALT_DMA) : 0; - mb(); - /* zero anything else */ - ASC_TC_PUT(regs, 0); mb(); - regs->asc_syn_p = asc_min_period; mb(); - regs->asc_syn_o = 0; mb(); /* asynch for now */ - - regs->asc_cmd = ASC_CMD_ENABLE_SEL; mb(); - - if (quick) return; - - /* - * reset the scsi bus, the interrupt routine does the rest - * or you can call asc_bus_reset(). - */ - regs->asc_cmd = ASC_CMD_BUS_RESET; mb(); -} - - -/* - * Operational functions - */ - -/* - * Start a SCSI command on a target - */ -asc_go(tgt, cmd_count, in_count, cmd_only) - target_info_t *tgt; - boolean_t cmd_only; -{ - asc_softc_t asc; - register spl_t s; - boolean_t disconn; - script_t scp; - boolean_t (*handler)(); - - LOG(1,"go"); - - asc = (asc_softc_t)tgt->hw_state; - - tgt->transient_state.cmd_count = cmd_count; /* keep it here */ - tgt->transient_state.out_count = 0; /* default */ - - (*asc->dma_ops->map)(asc->dma_state, tgt); - - disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id); - disconn = disconn && (asc->ntargets > 1); - disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); - - /* - * Setup target state - */ - tgt->done = SCSI_RET_IN_PROGRESS; - - handler = (disconn) ? asc_err_disconn : asc_err_generic; - - switch (tgt->cur_cmd) { - case SCSI_CMD_READ: - case SCSI_CMD_LONG_READ: - LOG(2,"readop"); - scp = asc_script_data_in; - break; - case SCSI_CMD_WRITE: - case SCSI_CMD_LONG_WRITE: - LOG(0x18,"writeop"); - scp = asc_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 = asc_script_try_synch; - tgt->flags |= TGT_TRY_SYNCH; - disconn = FALSE; - 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 = asc_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 = asc_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 = asc_script_simple_cmd; - } else { - scp = asc_script_try_synch; - tgt->flags |= TGT_TRY_SYNCH; - cmd_only = FALSE; - disconn = FALSE; - } - LOG(0x1c,"cmdop"); - LOG(0x80+tgt->cur_cmd,0); - break; - default: - LOG(0x1c,"cmdop"); - LOG(0x80+tgt->cur_cmd,0); - scp = asc_script_simple_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; - - /* - * See if another target is currently selected on - * this SCSI bus, e.g. lock the asc 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(); - - if (asc->wd.nactive++ == 0) - asc->wd.watchdog_state = SCSI_WD_ACTIVE; - - if (asc->state & ASC_STATE_BUSY) { - /* - * Queue up this target, note that this takes care - * of proper FIFO scheduling of the scsi-bus. - */ - LOG(3,"enqueue"); - enqueue_tail(&asc->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. - */ - asc->state |= ASC_STATE_BUSY; - asc->next_target = tgt; - asc_attempt_selection(asc); - /* - * Note that we might still lose arbitration.. - */ - } - splx(s); -} - -asc_attempt_selection(asc) - asc_softc_t asc; -{ - target_info_t *tgt; - asc_padded_regmap_t *regs; - register int out_count; - - regs = asc->regs; - tgt = asc->next_target; - - LOG(4,"select"); - LOG(0x80+tgt->target_id,0); - - /* - * We own the bus now.. unless we lose arbitration - */ - asc->active_target = tgt; - - /* Try to avoid reselect collisions */ - if ((regs->asc_csr & (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) == - (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) - return; - - /* - * Cleanup the FIFO - */ - regs->asc_cmd = ASC_CMD_FLUSH; - readback(regs->asc_cmd); - /* - * This value is not from spec, I have seen it failing - * without this delay and with logging disabled. That had - * about 42 extra instructions @25Mhz. - */ - delay(2);/* XXX time & move later */ - - - /* - * Init bus state variables - */ - asc->script = tgt->transient_state.script; - asc->error_handler = tgt->transient_state.handler; - asc->done = SCSI_RET_IN_PROGRESS; - - asc->out_count = 0; - asc->in_count = 0; - asc->extra_count = 0; - - /* - * Start the chip going - */ - out_count = (*asc->dma_ops->start_cmd)(asc->dma_state, tgt); - if (tgt->transient_state.identify != 0xff) { - regs->asc_fifo = tgt->transient_state.identify | tgt->lun; - mb(); - } - ASC_TC_PUT(regs, out_count); - readback(regs->asc_cmd); - - regs->asc_dbus_id = tgt->target_id; - readback(regs->asc_dbus_id); - - regs->asc_sel_timo = asc->timeout; - readback(regs->asc_sel_timo); - - regs->asc_syn_p = tgt->sync_period; - readback(regs->asc_syn_p); - - regs->asc_syn_o = tgt->sync_offset; - readback(regs->asc_syn_o); - - /* ugly little help for compiler */ -#define command out_count - if (tgt->flags & TGT_DID_SYNCH) { - command = (tgt->transient_state.identify == 0xff) ? - ASC_CMD_SEL | ASC_CMD_DMA : - ASC_CMD_SEL_ATN | ASC_CMD_DMA; /*preferred*/ - } else if (tgt->flags & TGT_TRY_SYNCH) - command = ASC_CMD_SEL_ATN_STOP; - else - command = ASC_CMD_SEL | ASC_CMD_DMA; - - /* Try to avoid reselect collisions */ - if ((regs->asc_csr & (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) != - (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) { - register int tmp; - - regs->asc_cmd = command; mb(); - /* - * Very nasty but infrequent problem here. We got/will get - * reconnected but the chip did not interrupt. The watchdog would - * fix it allright, but it stops the machine before it expires! - * Too bad we cannot just look at the interrupt register, sigh. - */ - tmp = get_reg(regs,asc_cmd); - if ((tmp != command) && (tmp == (ASC_CMD_NOP|ASC_CMD_DMA))) { - if ((regs->asc_csr & ASC_CSR_INT) == 0) { - delay(250); /* increase if in trouble */ - - if (get_reg(regs,asc_csr) == SCSI_PHASE_MSG_IN) { - /* ok, take the risk of reading the ir */ - tmp = get_reg(regs,asc_intr); mb(); - if (tmp & ASC_INT_RESEL) { - (void) asc_reconnect(asc, get_reg(regs,asc_csr), tmp); - asc_wait(regs, ASC_CSR_INT, 1); - tmp = get_reg(regs,asc_intr); mb(); - regs->asc_cmd = ASC_CMD_MSG_ACPT; - readback(regs->asc_cmd); - } else /* does not happen, but who knows.. */ - asc_reset(asc,FALSE,asc->state & ASC_STATE_SPEC_DMA); - } - } - } - } -#undef command -} - -/* - * Interrupt routine - * Take interrupts from the chip - * - * Implementation: - * Move along the current command's script if - * all is well, invoke error handler if not. - */ -asc_intr(unit, spllevel) - spl_t spllevel; -{ - register asc_softc_t asc; - register script_t scp; - register int ir, csr; - register asc_padded_regmap_t *regs; -#if defined(HAS_MAPPED_SCSI) - extern boolean_t rz_use_mapped_interface; - - if (rz_use_mapped_interface) - return ASC_intr(unit,spllevel); -#endif - - asc = asc_softc[unit]; - - LOG(5,"\n\tintr"); - /* collect ephemeral information */ - regs = asc->regs; - csr = get_reg(regs,asc_csr); mb(); - asc->ss_was = get_reg(regs,asc_ss); mb(); - asc->cmd_was = get_reg(regs,asc_cmd); mb(); - - /* drop spurious interrupts */ - if ((csr & ASC_CSR_INT) == 0) - return; - - ir = get_reg(regs,asc_intr); /* this re-latches CSR (and SSTEP) */ - mb(); - -TR(csr);TR(ir);TR(get_reg(regs,asc_cmd));TRCHECK - - /* this must be done within 250msec of disconnect */ - if (ir & ASC_INT_DISC) { - regs->asc_cmd = ASC_CMD_ENABLE_SEL; - readback(regs->asc_cmd); - } - - if (ir & ASC_INT_RESET) - return asc_bus_reset(asc); - - /* we got an interrupt allright */ - if (asc->active_target) - asc->wd.watchdog_state = SCSI_WD_ACTIVE; - -#if mips - splx(spllevel); /* drop priority */ -#endif - - if ((asc->state & ASC_STATE_TARGET) || - (ir & (ASC_INT_SEL|ASC_INT_SEL_ATN))) - return asc_target_intr(asc, csr, ir); - - /* - * In attempt_selection() we could not check the asc_intr - * register to see if a reselection was in progress [else - * we would cancel the interrupt, and it would not be safe - * anyways]. So we gave the select command even if sometimes - * the chip might have been reconnected already. What - * happens then is that we get an illegal command interrupt, - * which is why the second clause. Sorry, I'd do it better - * if I knew of a better way. - */ - if ((ir & ASC_INT_RESEL) || - ((ir & ASC_INT_ILL) && (regs->asc_cmd & ASC_CMD_SEL_ATN))) - return asc_reconnect(asc, csr, ir); - - /* - * Check for various errors - */ - if ((csr & (ASC_CSR_GE|ASC_CSR_PE)) || (ir & ASC_INT_ILL)) { - char *msg; -printf("{E%x,%x}", csr, ir); - if (csr & ASC_CSR_GE) - return;/* sit and prey? */ - - if (csr & ASC_CSR_PE) - msg = "SCSI bus parity error"; - if (ir & ASC_INT_ILL) - msg = "Chip sez Illegal Command"; - /* all we can do is to throw a reset on the bus */ - printf( "asc%d: %s%s", asc - asc_softc_data, msg, - ", attempting recovery.\n"); - asc_reset(asc, FALSE, asc->state & ASC_STATE_SPEC_DMA); - return; - } - - if ((scp = asc->script) == 0) /* sanity */ - return; - - LOG(6,"match"); - if (SCRIPT_MATCH(csr,ir,scp->condition)) { - /* - * Perform the appropriate operation, - * then proceed - */ - if ((*scp->action)(asc, csr, ir)) { - asc->script = scp + 1; - regs->asc_cmd = scp->command; mb(); - } - } else - (void) (*asc->error_handler)(asc, csr, ir); -} - -asc_target_intr(asc, csr, ir) - register asc_softc_t asc; - -{ - register script_t scp; - - LOG(0x1e,"tmode"); - - if ((asc->state & ASC_STATE_TARGET) == 0) { - - /* - * We just got selected - */ - asc->state |= ASC_STATE_TARGET; - - /* - * See if this selection collided with our selection attempt - */ - if (asc->state & ASC_STATE_BUSY) - asc->state |= ASC_STATE_COLLISION; - asc->state |= ASC_STATE_BUSY; - - return asc_selected(asc, csr, ir); - - } - /* We must be executing a script here */ - scp = asc->script; - assert(scp != 0); - - LOG(6,"match"); - if (SCRIPT_MATCH(csr,ir,scp->condition)) { - /* - * Perform the appropriate operation, - * then proceed - */ - if ((*scp->action)(asc, csr, ir)) { - asc->script = scp + 1; - asc->regs->asc_cmd = scp->command; mb(); - } - } else - (void) (*asc->error_handler)(asc, csr, ir); - -} - -/* - * All the many little things that the interrupt - * routine might switch to - */ -boolean_t -asc_clean_fifo(asc, csr, ir) - register asc_softc_t asc; - -{ - register asc_padded_regmap_t *regs = asc->regs; - register char ff; - - ASC_TC_GET(regs,asc->dmacnt_at_end); - - ASC_TC_PUT(regs,0); /* stop dma engine */ - readback(regs->asc_cmd); - - LOG(7,"clean_fifo"); - - while (fifo_count(regs)) { - ff = get_fifo(regs); - mb(); - } - return TRUE; -} - -boolean_t -asc_end(asc, csr, ir) - register asc_softc_t asc; -{ - register target_info_t *tgt; - register io_req_t ior; - - LOG(8,"end"); - - asc->state &= ~ASC_STATE_TARGET; - asc->regs->asc_syn_p = 0; mb(); - asc->regs->asc_syn_o = 0; mb(); - - tgt = asc->active_target; - if ((tgt->done = asc->done) == SCSI_RET_IN_PROGRESS) - tgt->done = SCSI_RET_SUCCESS; - - asc->script = 0; - - if (asc->wd.nactive-- == 1) - asc->wd.watchdog_state = SCSI_WD_INACTIVE; - - asc_release_bus(asc); - - if (ior = tgt->ior) { - /* - * WARNING: the above might have scheduled the - * DMA engine off to someone else. Keep it in - * mind in the following code - */ - (*asc->dma_ops->end_cmd)(asc->dma_state, tgt, ior); - - LOG(0xA,"ops->restart"); - (*tgt->dev_ops->restart)( tgt, TRUE); - } - - return FALSE; -} - -boolean_t -asc_release_bus(asc) - register asc_softc_t asc; -{ - boolean_t ret = TRUE; - - LOG(9,"release"); - if (asc->state & ASC_STATE_COLLISION) { - - LOG(0xB,"collided"); - asc->state &= ~ASC_STATE_COLLISION; - asc_attempt_selection(asc); - - } else if (queue_empty(&asc->waiting_targets)) { - - asc->state &= ~ASC_STATE_BUSY; - asc->active_target = 0; - asc->script = 0; - ret = FALSE; - - } else { - - LOG(0xC,"dequeue"); - asc->next_target = (target_info_t *) - dequeue_head(&asc->waiting_targets); - asc_attempt_selection(asc); - } - return ret; -} - -boolean_t -asc_get_status(asc, csr, ir) - register asc_softc_t asc; -{ - register asc_padded_regmap_t *regs = asc->regs; - register scsi2_status_byte_t status; - int len; - boolean_t ret; - io_req_t ior; - register target_info_t *tgt = asc->active_target; - - LOG(0xD,"get_status"); -TRWRAP - - asc->state &= ~ASC_STATE_DMA_IN; - - if (asc->state & ASC_STATE_DO_RFB) { - tgt->transient_state.has_oddb = FALSE; - regs->asc_cnfg2 = ASC_CNFG2_SCSI2; - } - - /* - * Get the last two bytes in FIFO - */ - while (fifo_count(regs) > 2) { - status.bits = get_fifo(regs); mb(); - } - - status.bits = get_fifo(regs); mb(); - - if (status.st.scsi_status_code != SCSI_ST_GOOD) { - scsi_error(asc->active_target, SCSI_ERR_STATUS, status.bits, 0); - asc->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? - SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; - } else - asc->done = SCSI_RET_SUCCESS; - - status.bits = get_fifo(regs); /* just pop the command_complete */ - mb(); - - /* if reading, move the last piece of data in main memory */ - if (len = asc->in_count) { - register int count; - - count = asc->dmacnt_at_end; - if (count) { -#if 0 - this is incorrect and besides.. - tgt->ior->io_residual = count; -#endif - len -= count; - } - regs->asc_cmd = asc->script->command; - readback(regs->asc_cmd); - - ret = FALSE; - } else - ret = TRUE; - - asc->dmacnt_at_end = 0; - (*asc->dma_ops->end_xfer)(asc->dma_state, tgt, len); - if (!ret) - asc->script++; - return ret; -} - -boolean_t -asc_put_status(asc, csr, ir) - register asc_softc_t asc; -{ - register asc_padded_regmap_t *regs = asc->regs; - register scsi2_status_byte_t status; - register target_info_t *tgt = asc->active_target; - int len; - - LOG(0x21,"put_status"); - - asc->state &= ~ASC_STATE_DMA_IN; - - if (len = asc->in_count) { - register int count; - - ASC_TC_GET(regs,count); mb(); - if (count) - len -= count; - } - (*asc->dma_ops->end_xfer)(asc->dma_state, tgt, len); - -/* status.st.scsi_status_code = SCSI_ST_GOOD; */ - regs->asc_fifo = 0; mb(); - regs->asc_fifo = SCSI_COMMAND_COMPLETE; mb(); - - return TRUE; -} - - -boolean_t -asc_dma_in(asc, csr, ir) - register asc_softc_t asc; -{ - register target_info_t *tgt; - register asc_padded_regmap_t *regs = asc->regs; - register int count; - unsigned char ff = get_reg(regs,asc_flags); mb(); - - LOG(0xE,"dma_in"); - tgt = asc->active_target; - - /* - * This seems to be needed on certain rudimentary targets - * (such as the DEC TK50 tape) which apparently only pick - * up 6 initial bytes: when you add the initial IDENTIFY - * you are left with 1 pending byte, which is left in the - * FIFO and would otherwise show up atop the data we are - * really requesting. - * - * This is only speculation, though, based on the fact the - * sequence step value of 3 out of select means the target - * changed phase too quick and some bytes have not been - * xferred (see NCR manuals). Counter to this theory goes - * the fact that the extra byte that shows up is not alwyas - * zero, and appears to be pretty random. - * Note that asc_flags say there is one byte in the FIFO - * even in the ok case, but the sstep value is the right one. - * Note finally that this might all be a sync/async issue: - * I have only checked the ok case on synch disks so far. - * - * Indeed it seems to be an asynch issue: exabytes do it too. - */ - if ((tgt->sync_offset == 0) && ((ff & ASC_FLAGS_SEQ_STEP) != 0x80)) { - regs->asc_cmd = ASC_CMD_NOP; - wbflush(); - PRINT(("[tgt %d: %x while %d]", tgt->target_id, ff, tgt->cur_cmd)); - while ((ff & ASC_FLAGS_FIFO_CNT) != 0) { - ff = get_fifo(regs); mb(); - ff = get_reg(regs,asc_flags); mb(); - } - } - - asc->state |= ASC_STATE_DMA_IN; - - count = (*asc->dma_ops->start_datain)(asc->dma_state, tgt); - ASC_TC_PUT(regs, count); - readback(regs->asc_cmd); - - if ((asc->in_count = count) == tgt->transient_state.in_count) - return TRUE; - regs->asc_cmd = asc->script->command; mb(); - asc->script = asc_script_restart_data_in; - return FALSE; -} - -asc_dma_in_r(asc, csr, ir) - register asc_softc_t asc; -{ - register target_info_t *tgt; - register asc_padded_regmap_t *regs = asc->regs; - register int count; - boolean_t advance_script = TRUE; - - - LOG(0x1f,"dma_in_r"); - tgt = asc->active_target; - - asc->state |= ASC_STATE_DMA_IN; - - if (asc->in_count == 0) { - /* - * Got nothing yet, we just reconnected. - */ - register int avail; - - /* - * NOTE: if we have to handle the RFB (obb), - * the odd byte has been installed at reconnect - * time, before switching to data-in phase. Now - * we are already in data-in phase. - * It is up to the DMA engine to trim the dma_ptr - * down one byte. - */ - - count = (*asc->dma_ops->restart_datain_1) - (asc->dma_state, tgt); - - /* common case of 8k-or-less read ? */ - advance_script = (tgt->transient_state.in_count == count); - - } else { - - /* - * We received some data. - */ - register int offset, xferred; - - /* - * Problem: sometimes we get a 'spurious' interrupt - * right after a reconnect. It goes like disconnect, - * reconnect, dma_in_r, here but DMA is still rolling. - * Since there is no good reason we got here to begin with - * we just check for the case and dismiss it: we should - * get another interrupt when the TC goes to zero or the - * target disconnects. - */ - ASC_TC_GET(regs,xferred); mb(); - if (xferred != 0) - return FALSE; - - xferred = asc->in_count - xferred; - assert(xferred > 0); - - tgt->transient_state.in_count -= xferred; - assert(tgt->transient_state.in_count > 0); - - /* - * There should NOT be any obb issues here, - * we would have no control anyways. - */ - count = (*asc->dma_ops->restart_datain_2) - (asc->dma_state, tgt, xferred); - - asc->in_count = count; - ASC_TC_PUT(regs, count); - readback(regs->asc_cmd); - regs->asc_cmd = asc->script->command; mb(); - - (*asc->dma_ops->restart_datain_3) - (asc->dma_state, tgt); - - /* last chunk ? */ - if (count == tgt->transient_state.in_count) - asc->script++; - - return FALSE; - } - - asc->in_count = count; - ASC_TC_PUT(regs, count); - readback(regs->asc_cmd); - - if (!advance_script) { - regs->asc_cmd = asc->script->command; - readback(regs->asc_cmd); - } - return advance_script; -} - - -/* send data to target. Only called to start the xfer */ - -boolean_t -asc_dma_out(asc, csr, ir) - register asc_softc_t asc; -{ - register asc_padded_regmap_t *regs = asc->regs; - register int reload_count; - register target_info_t *tgt; - int command; - - LOG(0xF,"dma_out"); - - ASC_TC_GET(regs, reload_count); mb(); - asc->extra_count = fifo_count(regs); - reload_count += asc->extra_count; - ASC_TC_PUT(regs, reload_count); - asc->state &= ~ASC_STATE_DMA_IN; - readback(regs->asc_cmd); - - tgt = asc->active_target; - - command = asc->script->command; - - if (reload_count == 0) reload_count = ASC_TC_MAX; - asc->out_count = reload_count; - - if (reload_count >= tgt->transient_state.out_count) - asc->script++; - else - asc->script = asc_script_restart_data_out; - - if ((*asc->dma_ops->start_dataout) - (asc->dma_state, tgt, (volatile unsigned *)®s->asc_cmd, - command, &asc->extra_count)) { - regs->asc_cmd = command; - readback(regs->asc_cmd); - } - - return FALSE; -} - -/* send data to target. Called in two different ways: - (a) to restart a big transfer and - (b) after reconnection - */ -boolean_t -asc_dma_out_r(asc, csr, ir) - register asc_softc_t asc; -{ - register asc_padded_regmap_t *regs = asc->regs; - register target_info_t *tgt; - boolean_t advance_script = TRUE; - int count; - - - LOG(0x20,"dma_out_r"); - - tgt = asc->active_target; - asc->state &= ~ASC_STATE_DMA_IN; - - if (asc->out_count == 0) { - /* - * Nothing committed: we just got reconnected - */ - count = (*asc->dma_ops->restart_dataout_1) - (asc->dma_state, tgt); - - /* is this the last chunk ? */ - advance_script = (tgt->transient_state.out_count == count); - } else { - /* - * We sent some data. - */ - register int offset, xferred; - - ASC_TC_GET(regs,count); mb(); - - /* see comment above */ - if (count) { - return FALSE; - } - - count += fifo_count(regs); - count -= asc->extra_count; - xferred = asc->out_count - count; - assert(xferred > 0); - - tgt->transient_state.out_count -= xferred; - assert(tgt->transient_state.out_count > 0); - - count = (*asc->dma_ops->restart_dataout_2) - (asc->dma_state, tgt, xferred); - - /* last chunk ? */ - if (tgt->transient_state.out_count == count) - goto quickie; - - asc->out_count = count; - - asc->extra_count = (*asc->dma_ops->restart_dataout_3) - (asc->dma_state, tgt, - (volatile unsigned *)®s->asc_fifo); - ASC_TC_PUT(regs, count); - readback(regs->asc_cmd); - regs->asc_cmd = asc->script->command; mb(); - - (*asc->dma_ops->restart_dataout_4)(asc->dma_state, tgt); - - return FALSE; - } - -quickie: - asc->extra_count = (*asc->dma_ops->restart_dataout_3) - (asc->dma_state, tgt, - (volatile unsigned *)®s->asc_fifo); - - asc->out_count = count; - - ASC_TC_PUT(regs, count); - readback(regs->asc_cmd); - - if (!advance_script) { - regs->asc_cmd = asc->script->command; - } - return advance_script; -} - -boolean_t -asc_dosynch(asc, csr, ir) - register asc_softc_t asc; - register unsigned char csr, ir; -{ - register asc_padded_regmap_t *regs = asc->regs; - register unsigned char c; - int i, per, offs; - register target_info_t *tgt; - - /* - * Phase is MSG_OUT here. - * Try to go synchronous, unless prohibited - */ - tgt = asc->active_target; - regs->asc_cmd = ASC_CMD_FLUSH; - readback(regs->asc_cmd); - delay(1); - - per = asc_min_period; - if (BGET(scsi_no_synchronous_xfer,asc->sc->masterno,tgt->target_id)) - offs = 0; - else - offs = asc_max_offset; - - tgt->flags |= TGT_DID_SYNCH; /* only one chance */ - tgt->flags &= ~TGT_TRY_SYNCH; - - regs->asc_fifo = SCSI_EXTENDED_MESSAGE; mb(); - regs->asc_fifo = 3; mb(); - regs->asc_fifo = SCSI_SYNC_XFER_REQUEST; mb(); - regs->asc_fifo = asc_to_scsi_period(asc_min_period,asc->clk); mb(); - regs->asc_fifo = offs; mb(); - regs->asc_cmd = ASC_CMD_XFER_INFO; - readback(regs->asc_cmd); - csr = asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr); mb(); - - /* some targets might be slow to move to msg-in */ - - if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) { - - /* wait for direction bit to flip */ - csr = asc_wait(regs, SCSI_IO, 0); - ir = get_reg(regs,asc_intr); mb(); - /* Some ugly targets go stright to command phase. - "You could at least say goodbye" */ - if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) - goto did_not; - if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) - gimmeabreak(); - } - - regs->asc_cmd = ASC_CMD_XFER_INFO; mb(); - csr = asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr); mb(); - - /* some targets do not even take all the bytes out */ - while (fifo_count(regs) > 0) { - c = get_fifo(regs); /* see what it says */ - mb(); - } - - if (c == SCSI_MESSAGE_REJECT) { -did_not: - printf(" did not like SYNCH xfer "); - - /* Tk50s get in trouble with ATN, sigh. */ - regs->asc_cmd = ASC_CMD_CLR_ATN; - readback(regs->asc_cmd); - - goto cmd; - } - - /* - * Receive the rest of the message - */ - regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); - asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr); mb(); - - if (c != SCSI_EXTENDED_MESSAGE) - gimmeabreak(); - - regs->asc_cmd = ASC_CMD_XFER_INFO; mb(); - asc_wait(regs, ASC_CSR_INT, 1); - c = get_reg(regs,asc_intr); mb(); - if (get_fifo(regs) != 3) - panic("asc_dosynch"); - - for (i = 0; i < 3; i++) { - regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); - asc_wait(regs, ASC_CSR_INT, 1); - c = get_reg(regs,asc_intr); mb(); - - regs->asc_cmd = ASC_CMD_XFER_INFO; mb(); - asc_wait(regs, ASC_CSR_INT, 1); - c = get_reg(regs,asc_intr);/*ack*/ mb(); - c = get_fifo(regs); mb(); - - if (i == 1) tgt->sync_period = scsi_period_to_asc(c,asc->clk); - if (i == 2) tgt->sync_offset = c; - } - -cmd: - regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); - csr = asc_wait(regs, ASC_CSR_INT, 1); - c = get_reg(regs,asc_intr); mb(); - - /* Might have to wait a bit longer for slow targets */ - for (c = 0; SCSI_PHASE(get_reg(regs,asc_csr)) == SCSI_PHASE_MSG_IN; c++) { - mb(); - delay(2); - if (c & 0x80) break; /* waited too long */ - } - csr = get_reg(regs,asc_csr); mb(); - - /* phase should normally be command here */ - if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) { - register char *cmd = tgt->cmd_ptr; - - /* test unit ready or inquiry */ - for (i = 0; i < sizeof(scsi_command_group_0); i++) { - regs->asc_fifo = *cmd++; mb(); - } - ASC_TC_PUT(regs,0xff); mb(); - regs->asc_cmd = ASC_CMD_XFER_PAD; /*0x18*/ mb(); - - if (tgt->cur_cmd == SCSI_CMD_INQUIRY) { - tgt->transient_state.script = asc_script_data_in; - asc->script = tgt->transient_state.script; - regs->asc_syn_p = tgt->sync_period; - regs->asc_syn_o = tgt->sync_offset; mb(); - return FALSE; - } - - csr = asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr);/*ack*/ mb(); - } - - if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) - csr = asc_wait(regs, SCSI_IO, 1); /* direction flip */ - -status: - if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) - gimmeabreak(); - - return TRUE; -} - -/* The other side of the coin.. */ -asc_odsynch(asc, initiator) - register asc_softc_t asc; - target_info_t *initiator; -{ - register asc_padded_regmap_t *regs = asc->regs; - register unsigned char c; - int len, per, offs; - - /* - * Phase is MSG_OUT, we are the target and we have control. - * Any IDENTIFY messages have been handled already. - */ - initiator->flags |= TGT_DID_SYNCH; - initiator->flags &= ~TGT_TRY_SYNCH; - - /* - * We only understand synch negotiations - */ - c = get_fifo(regs); mb(); - if (c != SCSI_EXTENDED_MESSAGE) goto bad; - - /* - * This is not in the specs, but apparently the chip knows - * enough about scsi to receive the length automatically. - * So there were two bytes in the fifo at function call. - */ - len = get_fifo(regs); mb(); - if (len != 3) goto bad; - while (len) { - if (fifo_count(regs) == 0) { - regs->asc_cmd = ASC_CMD_RCV_MSG; - readback(regs->asc_cmd); - asc_wait(regs, ASC_CSR_INT, 1); - c = get_reg(regs,asc_intr); mb(); - } - c = get_fifo(regs); mb(); - if (len == 1) offs = c; - if (len == 2) per = c; - len--; - } - - /* - * Adjust the proposed parameters - */ - c = scsi_period_to_asc(per,asc->clk); - initiator->sync_period = c; - per = asc_to_scsi_period(c,asc->clk); - - if (offs > asc_max_offset) offs = asc_max_offset; - initiator->sync_offset = offs; - - /* - * Tell him what the deal is - */ - regs->asc_fifo = SCSI_EXTENDED_MESSAGE; mb(); - regs->asc_fifo = 3; mb(); - regs->asc_fifo = SCSI_SYNC_XFER_REQUEST; mb(); - regs->asc_fifo = per; mb(); - regs->asc_fifo = offs; mb(); - regs->asc_cmd = ASC_CMD_SND_MSG; - readback(regs->asc_cmd); - asc_wait(regs, ASC_CSR_INT, 1); - c = get_reg(regs,asc_intr); mb(); - - /* - * Exit conditions: fifo empty, phase undefined but non-command - */ - return; - - /* - * Something wrong, reject the message - */ -bad: - while (fifo_count(regs)) { - c = get_fifo(regs); mb(); - } - regs->asc_fifo = SCSI_MESSAGE_REJECT; mb(); - regs->asc_cmd = ASC_CMD_SND_MSG; - readback(regs->asc_cmd); - asc_wait(regs, ASC_CSR_INT, 1); - c = get_reg(regs,asc_intr); mb(); -} - -/* - * The bus was reset - */ -asc_bus_reset(asc) - register asc_softc_t asc; -{ - register asc_padded_regmap_t *regs = asc->regs; - - LOG(0x1d,"bus_reset"); - - /* - * Clear bus descriptor - */ - asc->script = 0; - asc->error_handler = 0; - asc->active_target = 0; - asc->next_target = 0; - asc->state &= ASC_STATE_SPEC_DMA | ASC_STATE_DO_RFB; - queue_init(&asc->waiting_targets); - asc->wd.nactive = 0; - asc_reset(asc, TRUE, asc->state & ASC_STATE_SPEC_DMA); - - printf("asc: (%d) bus reset ", ++asc->wd.reset_count); - - /* some targets take long to reset */ - delay( scsi_delay_after_reset + - asc->sc->initiator_id * 500000); /* if multiple initiators */ - - if (asc->sc == 0) /* sanity */ - return; - - scsi_bus_was_reset(asc->sc); -} - -/* - * Disconnect/reconnect mode ops - */ - -/* get the message in via dma */ -boolean_t -asc_msg_in(asc, csr, ir) - register asc_softc_t asc; - register unsigned char csr, ir; -{ - register target_info_t *tgt; - register asc_padded_regmap_t *regs = asc->regs; - unsigned char ff; - - LOG(0x10,"msg_in"); - /* must clean FIFO as in asc_dma_in, sigh */ - while (fifo_count(regs) != 0) { - ff = get_fifo(regs); mb(); - } - - (void) (*asc->dma_ops->start_msgin)(asc->dma_state, asc->active_target); - /* We only really expect two bytes, at tgt->cmd_ptr */ - ASC_TC_PUT(regs, sizeof(scsi_command_group_0)); - readback(regs->asc_cmd); - - return TRUE; -} - -/* check the message is indeed a DISCONNECT */ -boolean_t -asc_disconnect(asc, csr, ir) - register asc_softc_t asc; - register unsigned char csr, ir; - -{ - register char *msgs, ff; - register target_info_t *tgt; - asc_padded_regmap_t *regs; - - tgt = asc->active_target; - - (*asc->dma_ops->end_msgin)(asc->dma_state, tgt); - - /* - * Do not do this. It is most likely a reconnection - * message that sits there already by the time we - * get here. The chip is smart enough to only dma - * the bytes that correctly came in as msg_in proper, - * the identify and selection bytes are not dma-ed. - * Yes, sometimes the hardware does the right thing. - */ -#if 0 - /* First check message got out of the fifo */ - regs = asc->regs; - while (fifo_count(regs) != 0) { - *msgs++ = get_fifo(regs); - } -#endif - msgs = tgt->cmd_ptr; - - /* A SDP message preceeds it in non-completed READs */ - if ((msgs[0] == SCSI_DISCONNECT) || /* completed */ - ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* non complete */ - (msgs[1] == SCSI_DISCONNECT))) { - /* that's the ok case */ - } else - printf("[tgt %d bad SDP: x%x]", - tgt->target_id, *((unsigned short *)msgs)); - - return TRUE; -} - -/* save all relevant data, free the BUS */ -boolean_t -asc_disconnected(asc, csr, ir) - register asc_softc_t asc; - register unsigned char csr, ir; - -{ - register target_info_t *tgt; - - LOG(0x11,"disconnected"); - asc_disconnect(asc,csr,ir); - - tgt = asc->active_target; - tgt->flags |= TGT_DISCONNECTED; - tgt->transient_state.handler = asc->error_handler; - /* anything else was saved in asc_err_disconn() */ - - PRINT(("{D%d}", tgt->target_id)); - - asc_release_bus(asc); - - return FALSE; -} - -int asc_restore_ptr = 1; - -/* get reconnect message out of fifo, restore BUS */ -boolean_t -asc_reconnect(asc, csr, ir) - register asc_softc_t asc; - register unsigned char csr, ir; - -{ - register target_info_t *tgt; - asc_padded_regmap_t *regs; - unsigned int id; - - LOG(0x12,"reconnect"); - /* - * See if this reconnection collided with a selection attempt - */ - if (asc->state & ASC_STATE_BUSY) - asc->state |= ASC_STATE_COLLISION; - - asc->state |= ASC_STATE_BUSY; - - /* find tgt: first byte in fifo is (tgt_id|our_id) */ - regs = asc->regs; - while (fifo_count(regs) > 2) /* sanity */ { - id = get_fifo(regs); mb(); - } - if (fifo_count(regs) != 2) - gimmeabreak(); - - id = get_fifo(regs); /* must decode this now */ - mb(); - id &= ~(1 << asc->sc->initiator_id); - { - register int i; - for (i = 0; id > 1; i++) - id >>= 1; - id = i; - } - - tgt = asc->sc->target[id]; - if (tgt == 0) panic("asc_reconnect"); - - /* synch things*/ - regs->asc_syn_p = tgt->sync_period; - regs->asc_syn_o = tgt->sync_offset; - readback(regs->asc_syn_o); - - /* Get IDENTIFY message */ - { - register int i = get_fifo(regs); - if ((i & SCSI_IDENTIFY) == 0) - gimmeabreak(); - i &= 0x7; - /* If not LUN 0 see which target it is */ - if (i) { - target_info_t *tgt1; - for (tgt1 = tgt->next_lun; - tgt1 && tgt1 != tgt; - tgt1 = tgt1->next_lun) - if (tgt1->lun == i) { - tgt = tgt1; - break; - } - } - } - - if (asc->state & ASC_STATE_DO_RFB) { - if (tgt->transient_state.has_oddb) { - if (tgt->sync_period) { - regs->asc_cnfg2 = ASC_CNFG2_SCSI2 | ASC_CNFG2_RFB; - wbflush(); - regs->asc_rfb = tgt->transient_state.the_oddb; - } else { - regs->asc_fifo = tgt->transient_state.the_oddb; - } - tgt->transient_state.has_oddb = FALSE; - } else { - regs->asc_cnfg2 = ASC_CNFG2_SCSI2; - } - wbflush(); - } - - PRINT(("{R%d}", id)); - if (asc->state & ASC_STATE_COLLISION) - PRINT(("[B %d-%d]", asc->active_target->target_id, id)); - - LOG(0x80+id,0); - - asc->active_target = tgt; - tgt->flags &= ~TGT_DISCONNECTED; - - asc->script = tgt->transient_state.script; - asc->error_handler = tgt->transient_state.handler; - asc->in_count = 0; - asc->out_count = 0; - - regs->asc_cmd = ASC_CMD_MSG_ACPT; - readback(regs->asc_cmd); - - /* What if there is a RESTORE_PTR msgin ? */ - if (asc_restore_ptr) { -more_msgin: - csr = asc_wait(regs, ASC_CSR_INT, 1); - - if (SCSI_PHASE(csr) == SCSI_PHASE_MSG_IN) { - /* ack intr */ - id = get_reg(regs,asc_intr); mb(); - - /* Ok, get msg */ - regs->asc_cmd = ASC_CMD_XFER_INFO; - readback(regs->asc_cmd); - /* wait for xfer done */ - csr = asc_wait(regs, ASC_CSR_INT, 1); - - /* look at what we got */ - id = get_fifo(regs); - - if (id != SCSI_RESTORE_POINTERS) - printf("asc%d: uhu msg %x\n", asc->sc->masterno, id); - /* ack intr */ - id = get_reg(regs,asc_intr); mb(); - - /* move on */ - regs->asc_cmd = ASC_CMD_MSG_ACPT; - readback(regs->asc_cmd); - goto more_msgin; - } - } - - return FALSE; -} - - -/* We have been selected as target */ - -boolean_t -asc_selected(asc, csr, ir) - register asc_softc_t asc; - register unsigned csr, ir; -{ - register asc_padded_regmap_t *regs = asc->regs; - register unsigned char id; - target_info_t *self, *initiator; - unsigned int len; - - /* - * Find initiator's id: the head of the fifo is (init_id|our_id) - */ - - id = get_fifo(regs); /* must decode this now */ - mb(); - id &= ~(1 << asc->sc->initiator_id); - { - register int i; - for (i = 0; id > 1; i++) - id >>= 1; - id = i; - } - - /* - * See if we have seen him before - */ - self = asc->sc->target[asc->sc->initiator_id]; - initiator = asc->sc->target[id]; - if (initiator == 0) { - - initiator = scsi_slave_alloc(asc->sc->masterno, id, asc); - (*asc->dma_ops->new_target)(asc->dma_state, initiator); - - } - - if (! (initiator->flags & TGT_ONLINE) ) - sccpu_new_initiator(self, initiator); - - /* - * If selected with ATN the chip did the msg-out - * phase already, let us look at the message(s) - */ - if (ir & ASC_INT_SEL_ATN) { - register unsigned char m; - - m = get_fifo(regs); mb(); - if ((m & SCSI_IDENTIFY) == 0) - gimmeabreak(); - - csr = get_reg(regs,asc_csr); mb(); - if ((SCSI_PHASE(csr) == SCSI_PHASE_MSG_OUT) && - fifo_count(regs)) - asc_odsynch(asc, initiator); - - /* Get the command now, unless we have it already */ - mb(); - if (fifo_count(regs) < sizeof(scsi_command_group_0)) { - regs->asc_cmd = ASC_CMD_RCV_CMD; - readback(regs->asc_cmd); - asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr); mb(); - csr = get_reg(regs,asc_csr); mb(); - } - } else { - /* - * Pop away the null byte that follows the id - */ - if (get_fifo(regs) != 0) - gimmeabreak(); - - } - - /* - * Take rest of command out of fifo - */ - self->dev_info.cpu.req_pending = TRUE; - self->dev_info.cpu.req_id = id; - self->dev_info.cpu.req_cmd = get_fifo(regs); - self->dev_info.cpu.req_lun = get_fifo(regs); - - LOG(0x80+self->dev_info.cpu.req_cmd, 0); - - switch ((self->dev_info.cpu.req_cmd & SCSI_CODE_GROUP) >> 5) { - case 0: - len = get_fifo(regs) << 16; mb(); - len |= get_fifo(regs) << 8; mb(); - len |= get_fifo(regs); mb(); - break; - case 1: - case 2: - len = get_fifo(regs); /* xxx lba1 */ mb(); - len = get_fifo(regs); /* xxx lba2 */ mb(); - len = get_fifo(regs); /* xxx lba3 */ mb(); - len = get_fifo(regs); /* xxx lba4 */ mb(); - len = get_fifo(regs); /* xxx xxx */ mb(); - len = get_fifo(regs) << 8; mb(); - len |= get_fifo(regs); mb(); - break; - case 5: - len = get_fifo(regs); /* xxx lba1 */ mb(); - len = get_fifo(regs); /* xxx lba2 */ mb(); - len = get_fifo(regs); /* xxx lba3 */ mb(); - len = get_fifo(regs); /* xxx lba4 */ mb(); - len = get_fifo(regs) << 24; mb(); - len |= get_fifo(regs) << 16; mb(); - len |= get_fifo(regs) << 8; mb(); - len |= get_fifo(regs); mb(); - if (get_fifo(regs) != 0) ; - break; - default: - panic("asc_selected"); - } - self->dev_info.cpu.req_len = len; -/*if (scsi_debug) printf("[L x%x]", len);*/ - - /* xxx pop the cntrl byte away */ - if (get_fifo(regs) != 0) - gimmeabreak(); - mb(); - - /* - * Setup state - */ - asc->active_target = self; - asc->done = SCSI_RET_IN_PROGRESS; - asc->out_count = 0; - asc->in_count = 0; - asc->extra_count = 0; - - /* - * Sync params. - */ - regs->asc_syn_p = initiator->sync_period; - regs->asc_syn_o = initiator->sync_offset; - readback(regs->asc_syn_o); - - /* - * Do the up-call - */ - LOG(0xB,"tgt-mode-restart"); - (*self->dev_ops->restart)( self, FALSE); - - /* The call above has either prepared the data, - placing an ior on self, or it handled it some - other way */ - if (self->ior == 0) - return FALSE; - - /* sanity */ - if (fifo_count(regs)) { - regs->asc_cmd = ASC_CMD_FLUSH; - readback(regs->asc_cmd); - } - - /* needed by some dma code to align things properly */ - self->transient_state.cmd_count = sizeof(scsi_command_group_0); - self->transient_state.in_count = len; /* XXX */ - - (*asc->dma_ops->map)(asc->dma_state, self); - - if (asc->wd.nactive++ == 0) - asc->wd.watchdog_state = SCSI_WD_ACTIVE; - - - { - register script_t scp; - unsigned char command; - - switch (self->dev_info.cpu.req_cmd) { - case SCSI_CMD_TEST_UNIT_READY: - scp = asc_script_t_data_in+1; break; - case SCSI_CMD_SEND: - scp = asc_script_t_data_in; break; - default: - scp = asc_script_t_data_out; break; - } - - asc->script = scp; - command = scp->command; - if (!(*scp->action)(asc, csr, ir)) - return FALSE; - -/*if (scsi_debug) printf("[F%x]", fifo_count(regs));*/ - - asc->script++; - regs->asc_cmd = command; mb(); - - } - - return FALSE; -} - - -/* - * Other utilities - */ -private void -pick_up_oddb( - target_info_t *tgt) -{ - register char *lastp; - - /* State should have been updated before we get here */ - lastp = tgt->dma_ptr + tgt->transient_state.dma_offset; - - if ((vm_offset_t) lastp & 1) { - tgt->transient_state.has_oddb = TRUE; - tgt->transient_state.the_oddb = lastp[-1]; - } else - tgt->transient_state.has_oddb = FALSE; -} - - -/* - * Error handlers - */ - -/* - * Fall-back error handler. - */ -asc_err_generic(asc, csr, ir) - register asc_softc_t asc; -{ - LOG(0x13,"err_generic"); - - /* handle non-existant or powered off devices here */ - if ((ir == ASC_INT_DISC) && - (asc_isa_select(asc->cmd_was)) && - (ASC_SS(asc->ss_was) == 0)) { - /* Powered off ? */ - target_info_t *tgt = asc->active_target; - - tgt->flags = 0; - tgt->sync_period = 0; - tgt->sync_offset = 0; mb(); - asc->done = SCSI_RET_DEVICE_DOWN; - asc_end(asc, csr, ir); - return; - } - - switch (SCSI_PHASE(csr)) { - case SCSI_PHASE_STATUS: - if (asc->script[-1].condition == SCSI_PHASE_STATUS) { - /* some are just slow to get out.. */ - } else - asc_err_to_status(asc, csr, ir); - return; - break; - case SCSI_PHASE_DATAI: - if (asc->script->condition == SCSI_PHASE_STATUS) { - /* - * Here is what I know about it. We reconnect to - * the target (csr 87, ir 0C, cmd 44), start dma in - * (81 10 12) and then get here with (81 10 90). - * Dma is rolling and keeps on rolling happily, - * the value in the counter indicates the interrupt - * was signalled right away: by the time we get - * here only 80-90 bytes have been shipped to an - * rz56 running synchronous at 3.57 Mb/sec. - */ -/* printf("{P}");*/ - return; - } - break; - case SCSI_PHASE_DATAO: - if (asc->script->condition == SCSI_PHASE_STATUS) { - /* - * See comment above. Actually seen on hitachis. - */ -/* printf("{P}");*/ - return; - } - break; - case SCSI_PHASE_CMD: - /* This can be the case with drives that are not - even scsi-1 compliant and do not like to be - selected with ATN (to do the synch negot) and - go stright to command phase, regardless */ - - if (asc->script == asc_script_try_synch) { - - target_info_t *tgt = asc->active_target; - register asc_padded_regmap_t *regs = asc->regs; - - tgt->flags |= TGT_DID_SYNCH; /* only one chance */ - tgt->flags &= ~TGT_TRY_SYNCH; - - if (tgt->cur_cmd == SCSI_CMD_INQUIRY) - tgt->transient_state.script = asc_script_data_in; - else - tgt->transient_state.script = asc_script_simple_cmd; - asc->script = tgt->transient_state.script; - regs->asc_cmd = ASC_CMD_CLR_ATN; - readback(regs->asc_cmd); - asc->regs->asc_cmd = ASC_CMD_XFER_PAD|ASC_CMD_DMA; /*0x98*/ mb(); - printf(" did not like SYNCH xfer "); - return; - } - /* fall through */ - } - gimmeabreak(); -} - -/* - * Handle generic errors that are reported as - * an unexpected change to STATUS phase - */ -asc_err_to_status(asc, csr, ir) - register asc_softc_t asc; -{ - script_t scp = asc->script; - - LOG(0x14,"err_tostatus"); - while (scp->condition != SCSI_PHASE_STATUS) - scp++; - (*scp->action)(asc, csr, ir); - asc->script = scp + 1; - asc->regs->asc_cmd = scp->command; mb(); -#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 (xfer complete) -> reconnect - * and our script might expect at this point that the dma - * had to be restarted (it didn't notice it was completed). - * And in any event.. it is both correct and cleaner to - * declare error iff the STATUS byte says so. - */ - asc->done = SCSI_RET_NEED_SENSE; -#endif -} - -/* - * Handle disconnections as exceptions - */ -asc_err_disconn(asc, csr, ir) - register asc_softc_t asc; - register unsigned char csr, ir; -{ - register asc_padded_regmap_t *regs; - register target_info_t *tgt; - int count; - boolean_t callback = FALSE; - - LOG(0x16,"err_disconn"); - - /* - * We only do msg-in cases here - */ - if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) - return asc_err_generic(asc, csr, ir); - - regs = asc->regs; - tgt = asc->active_target; - - /* - * What did we expect to happen, and what did happen. - */ - switch (asc->script->condition) { - case SCSI_PHASE_DATAO: - /* - * A data out phase was either about to be started, - * or it was in progress but more had to go out later - * [e.g. a big xfer for instance, or more than the - * DMA engine can take in one shot]. - */ - LOG(0x1b,"+DATAO"); - if (asc->out_count) { - register int xferred, offset; - - /* - * Xfer in progress. See where we stopped. - */ - ASC_TC_GET(regs,xferred); /* temporary misnomer */ - /* - * Account for prefetching, in its various forms - */ - xferred += fifo_count(regs); - xferred -= asc->extra_count; - /* - * See how much went out, how much to go. - */ - xferred = asc->out_count - xferred; /* ok now */ - tgt->transient_state.out_count -= xferred; - assert(tgt->transient_state.out_count > 0); - - callback = (*asc->dma_ops->disconn_1) - (asc->dma_state, tgt, xferred); - - } else { - - /* - * A disconnection before DMA was (re)started. - */ - callback = (*asc->dma_ops->disconn_2) - (asc->dma_state, tgt); - - } - asc->extra_count = 0; - tgt->transient_state.script = asc_script_restart_data_out; - break; - - - case SCSI_PHASE_DATAI: - /* - * Same as above, the other way around - */ - LOG(0x17,"+DATAI"); - if (asc->in_count) { - register int offset, xferred; - - /* - * How much did we expect, how much did we get - */ - ASC_TC_GET(regs,count); mb(); - xferred = asc->in_count - count; - assert(xferred > 0); - -if (regs->asc_flags & 0xf) -printf("{Xf %x,%x,%x}", xferred, asc->in_count, fifo_count(regs)); - tgt->transient_state.in_count -= xferred; - assert(tgt->transient_state.in_count > 0); - - callback = (*asc->dma_ops->disconn_3) - (asc->dma_state, tgt, xferred); - - /* - * Handle obb if we have to. DMA code has - * updated pointers and flushed buffers. - */ - if (asc->state & ASC_STATE_DO_RFB) - pick_up_oddb(tgt); - - tgt->transient_state.script = asc_script_restart_data_in; - /* - * Some silly targets disconnect after they - * have given us all the data. - */ - if (tgt->transient_state.in_count == 0) - tgt->transient_state.script++; - - } else - tgt->transient_state.script = asc_script_restart_data_in; - break; - - case SCSI_PHASE_STATUS: - /* - * Either one of the above cases here. Only diff - * the DMA engine was setup to run to completion - * and (most likely) did not. - */ - ASC_TC_GET(regs,count); mb(); - if (asc->state & ASC_STATE_DMA_IN) { - register int offset, xferred; - - LOG(0x1a,"+STATUS+R"); - - - /* - * Handle brain-dead sequence: - * 1-xfer all data, disconnect - * 2-reconnect, disconnect immediately ?? - * 3-rept 2 - * 4-reconnect,complete - */ - if (asc->in_count) { - - xferred = asc->in_count - count; - assert(xferred > 0); -if (regs->asc_flags & 0xf) -printf("{Xf %x,%x,%x}", xferred, asc->in_count, fifo_count(regs)); - - tgt->transient_state.in_count -= xferred; - - callback = (*asc->dma_ops->disconn_4) - (asc->dma_state, tgt, xferred); - } - /* - * Handle obb if we have to. DMA code has - * updated pointers and flushed buffers. - */ - if (asc->state & ASC_STATE_DO_RFB) - pick_up_oddb(tgt); - - tgt->transient_state.script = asc_script_restart_data_in; - - /* see previous note */ - if (tgt->transient_state.in_count == 0) - tgt->transient_state.script++; - - } else { - - /* - * Outgoing xfer, take care of prefetching. - */ - /* add what's left in the fifo */ - count += fifo_count(regs); - /* take back the extra we might have added */ - count -= asc->extra_count; - /* ..and drop that idea */ - asc->extra_count = 0; - - LOG(0x19,"+STATUS+W"); - - /* - * All done ? This is less silly than with - * READs: some disks will only say "done" when - * the data is down on the platter, and some - * people like it much better that way. - */ - if ((count == 0) && (tgt->transient_state.out_count == asc->out_count)) { - /* all done */ - tgt->transient_state.script = asc->script; - tgt->transient_state.out_count = 0; - } else { - register int xferred, offset; - - /* how much we xferred */ - xferred = asc->out_count - count; - - /* how much to go */ - tgt->transient_state.out_count -= xferred; - assert(tgt->transient_state.out_count > 0); - - callback = (*asc->dma_ops->disconn_5) - (asc->dma_state,tgt,xferred); - - tgt->transient_state.script = asc_script_restart_data_out; - } - asc->out_count = 0; - } - break; - default: - gimmeabreak(); - return; - } - asc_msg_in(asc,csr,ir); - asc->script = asc_script_disconnect; - regs->asc_cmd = ASC_CMD_XFER_INFO|ASC_CMD_DMA; - wbflush(); - /* - * Prevent a race, now. If the reselection comes quickly - * the chip will prefetch and reload the transfer counter - * register. Make sure it will stop, by reloading a zero. - */ - regs->asc_tc_lsb = 0; - regs->asc_tc_msb = 0; - if (callback) - (*asc->dma_ops->disconn_callback)(asc->dma_state, tgt); -} - -/* - * Watchdog - * - * So far I have only seen the chip get stuck in a - * select/reselect conflict: the reselection did - * win and the interrupt register showed it but.. - * no interrupt was generated. - * But we know that some (name withdrawn) disks get - * stuck in the middle of dma phases... - */ -asc_reset_scsibus(asc) - register asc_softc_t asc; -{ - register target_info_t *tgt = asc->active_target; - register asc_padded_regmap_t *regs = asc->regs; - register int ir; - - if (scsi_debug && tgt) { - int dmalen; - ASC_TC_GET(asc->regs,dmalen); mb(); - printf("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, - asc->in_count, asc->out_count, - dmalen); - } - ir = get_reg(regs,asc_intr); mb(); - if ((ir & ASC_INT_RESEL) && (SCSI_PHASE(regs->asc_csr) == SCSI_PHASE_MSG_IN)) { - /* getting it out of the woods is a bit tricky */ - spl_t s = splbio(); - - (void) asc_reconnect(asc, get_reg(regs,asc_csr), ir); - asc_wait(regs, ASC_CSR_INT, 1); - ir = get_reg(regs,asc_intr); mb(); - regs->asc_cmd = ASC_CMD_MSG_ACPT; - readback(regs->asc_cmd); - splx(s); - } else { - regs->asc_cmd = ASC_CMD_BUS_RESET; mb(); - delay(35); - } -} - -#endif NASC > 0 - |