/* * 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 "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_7061_hdw.c * Author: Alessandro Forin, Carnegie Mellon University * Date: 10/90 * * Bottom layer of the SCSI driver: chip-dependent functions * * This file contains the code that is specific to the DEC DC7061 * 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 #if NSII > 0 #include #ifdef DECSTATION #define PAD(n) short n #endif #include /* spl definitions */ #include #include #include #include #include #include #include #include #define isa_oddbb hba_dep[0] #define oddbb hba_dep[1] #include #ifdef PAD typedef struct { volatile unsigned short sii_sdb; /* rw: Data bus and parity */ PAD(pad0); volatile unsigned short sii_sc1; /* rw: scsi signals 1 */ PAD(pad1); volatile unsigned short sii_sc2; /* rw: scsi signals 2 */ PAD(pad2); volatile unsigned short sii_csr; /* rw: control and status */ PAD(pad3); volatile unsigned short sii_id; /* rw: scsi bus ID */ PAD(pad4); volatile unsigned short sii_sel_csr; /* rw: selection status */ PAD(pad5); volatile unsigned short sii_destat; /* ro: selection detector status */ PAD(pad6); volatile unsigned short sii_dstmo; /* unsupp: dssi timeout */ PAD(pad7); volatile unsigned short sii_data; /* rw: data register */ PAD(pad8); volatile unsigned short sii_dma_ctrl; /* rw: dma control reg */ PAD(pad9); volatile unsigned short sii_dma_len; /* rw: length of transfer */ PAD(pad10); volatile unsigned short sii_dma_adr_low;/* rw: low address */ PAD(pad11); volatile unsigned short sii_dma_adr_hi; /* rw: high address */ PAD(pad12); volatile unsigned short sii_dma_1st_byte;/* rw: initial byte */ PAD(pad13); volatile unsigned short sii_stlp; /* unsupp: dssi short trgt list ptr */ PAD(pad14); volatile unsigned short sii_ltlp; /* unsupp: dssi long " " " */ PAD(pad15); volatile unsigned short sii_ilp; /* unsupp: dssi initiator list ptr */ PAD(pad16); volatile unsigned short sii_dssi_csr; /* unsupp: dssi control */ PAD(pad17); volatile unsigned short sii_conn_csr; /* rc: connection interrupt control */ PAD(pad18); volatile unsigned short sii_data_csr; /* rc: data interrupt control */ PAD(pad19); volatile unsigned short sii_cmd; /* rw: command register */ PAD(pad20); volatile unsigned short sii_diag_csr; /* rw: disgnostic status */ PAD(pad21); } sii_padded_regmap_t; #else /*!PAD*/ typedef sii_regmap_t sii_padded_regmap_t; #endif /*!PAD*/ #undef SII_CSR_SLE #define SII_CSR_SLE 0 /* for now */ #ifdef DECSTATION #include #define SII_OFFSET_RAM (KN01_SYS_SII_B_START-KN01_SYS_SII) #define SII_RAM_SIZE (KN01_SYS_SII_B_END-KN01_SYS_SII_B_START) /* 16 bits in 32 bit envelopes */ #define SII_DMADR_LO(ptr) ((((unsigned)ptr)>>1)&SII_DMA_LOW_MASK) #define SII_DMADR_HI(ptr) ((((unsigned)ptr)>>(16+1))&SII_DMA_HIGH_MASK) #endif /* DECSTATION */ #ifndef SII_OFFSET_RAM /* cross compile check */ #define SII_OFFSET_RAM 0 #define SII_RAM_SIZE 0x10000 #define SII_DMADR_LO(ptr) (((unsigned)ptr)>>16) #define SII_DMADR_HI(ptr) (((unsigned)ptr)&0xffff) #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 ((SII_RAM_SIZE/7) & ~(sizeof(int)-1)) /* * Round to 4k to make debug easier */ #define PER_TGT_BUFF_SIZE ((PER_TGT_DMA_SIZE >> 12) << 12) /* * Macros to make certain things a little more readable */ #define SII_COMMAND(regs,cs,ds,cmd) \ { \ (regs)->sii_cmd = ((cs) & 0x70) | \ ((ds) & 0x07) | (cmd); \ wbflush(); \ } #define SII_ACK(regs,cs,ds,cmd) \ { \ SII_COMMAND(regs,cs,ds,cmd); \ (regs)->sii_conn_csr = (cs); \ (regs)->sii_data_csr = (ds); \ } /* * Deal with bogus pmax dma buffer */ static char decent_buffer[NSII*8][256]; /* * 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 formed by the * values of the sii_conn_csr and sii_data_csr register * bits. The action part is just a function pointer, and the * command is what the 7061 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 sii_intr() for how and where this is all done. */ typedef struct script { int condition; /* expected state at interrupt */ int (*action)(); /* extra operations */ int command; /* command to the chip */ } *script_t; #define SCRIPT_MATCH(cs,ds) ((cs)&0x70|SCSI_PHASE((ds))) #define SII_PHASE_DISC 0x4 /* sort of .. */ /* When no command is needed */ #define SCRIPT_END -1 /* forward decls of script actions */ boolean_t sii_script_true(), /* when nothing needed */ sii_identify(), /* send identify msg */ sii_dosynch(), /* negotiate synch xfer */ sii_dma_in(), /* get data from target via dma */ sii_dma_out(), /* send data to target via dma */ sii_get_status(), /* get status from target */ sii_end_transaction(), /* all come to an end */ sii_msg_in(), /* get disconnect message(s) */ sii_disconnected(); /* current target disconnected */ /* forward decls of error handlers */ boolean_t sii_err_generic(), /* generic error handler */ sii_err_disconn(), /* when a target disconnects */ sii_err_rdp(), /* in reconn, handle rdp mgs */ gimmeabreak(); /* drop into the debugger */ int sii_reset_scsibus(); boolean_t sii_probe_target(); static sii_wait(); /* * State descriptor for this layer. There is one such structure * per (enabled) SCSI-7061 interface */ struct sii_softc { watchdog_t wd; sii_padded_regmap_t *regs; volatile char *buff; 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 SII_STATE_BUSY 0x01 /* selecting or currently connected */ #define SII_STATE_TARGET 0x04 /* currently selected as target */ #define SII_STATE_COLLISION 0x08 /* lost selection attempt */ #define SII_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ unsigned char ntargets; /* how many alive on this scsibus */ unsigned char done; unsigned char cmd_count; 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 */ } sii_softc_data[NSII]; typedef struct sii_softc *sii_softc_t; sii_softc_t sii_softc[NSII]; /* * Synch xfer parameters, and timing conversions */ int sii_min_period = 63; /* in 4 ns units */ int sii_max_offset = 3; /* pure number */ #define sii_to_scsi_period(a) (a) #define scsi_period_to_sii(p) (((p) < sii_min_period) ? sii_min_period : (p)) /* * Definition of the controller for the auto-configuration program. */ int sii_probe(), scsi_slave(), sii_go(), sii_intr(); extern void scsi_attach(); vm_offset_t sii_std[NSII] = { 0 }; struct bus_device *sii_dinfo[NSII*8]; struct bus_ctlr *sii_minfo[NSII]; struct bus_driver sii_driver = { sii_probe, scsi_slave, scsi_attach, sii_go, sii_std, "rz", sii_dinfo, "sii", sii_minfo, /*BUS_INTR_B4_PROBE?*/}; /* * Scripts */ struct script sii_script_data_in[] = { { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true, (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD}, { SCSI_PHASE_DATAI|SII_CON_CON, sii_dma_in, (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_DATAI}, { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status, SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS}, { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END} }, sii_script_data_out[] = { { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true, (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD}, { SCSI_PHASE_DATAO|SII_CON_CON, sii_dma_out, (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_DATAO}, { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status, SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS}, { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END} }, sii_script_cmd[] = { { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true, (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD}, { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status, SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS}, { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END} }, /* Same, after a disconnect */ sii_script_restart_data_in[] = { { SCSI_PHASE_DATAI|SII_CON_CON|SII_CON_DST, sii_dma_in, (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SII_CON_DST|SCSI_PHASE_DATAI}, { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status, SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS}, { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END} }, sii_script_restart_data_out[] = { { SCSI_PHASE_DATAO|SII_CON_CON|SII_CON_DST, sii_dma_out, (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SII_CON_DST|SCSI_PHASE_DATAO}, { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status, SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS}, { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END} }, sii_script_restart_cmd[] = { { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status, SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS}, { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END} }, /* Synchronous transfer negotiation */ sii_script_try_synch[] = { { SCSI_PHASE_MSG_OUT|SII_CON_CON, sii_dosynch, SCRIPT_END} }, /* Disconnect sequence */ sii_script_disconnect[] = { { SII_PHASE_DISC, sii_disconnected, SCRIPT_END} }; #define u_min(a,b) (((a) < (b)) ? (a) : (b)) #define DEBUG #ifdef DEBUG sii_state(regs) sii_padded_regmap_t *regs; { unsigned dmadr; if (regs == 0) regs = (sii_padded_regmap_t*) 0xba000000; dmadr = regs->sii_dma_adr_low | (regs->sii_dma_adr_hi << 16); db_printf("sc %x, dma %x @ x%X, cs %x, ds %x, cmd %x\n", (unsigned) regs->sii_sc1, (unsigned) regs->sii_dma_len, dmadr, (unsigned) regs->sii_conn_csr, (unsigned) regs->sii_data_csr, (unsigned) regs->sii_cmd); } sii_target_state(tgt) target_info_t *tgt; { if (tgt == 0) tgt = sii_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, 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 %X ", spt->condition, spt->command); db_printsym(spt->action,1); db_printf(", "); db_printsym(tgt->transient_state.handler, 1); db_printf("\n"); } return 0; } sii_all_targets(unit) { int i; target_info_t *tgt; for (i = 0; i < 8; i++) { tgt = sii_softc[unit]->sc->target[i]; if (tgt) sii_target_state(tgt); } } sii_script_state(unit) { script_t spt = sii_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(sii_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 sii_logpt; char sii_log[LOGSIZE]; #define MAXLOG_VALUE 0x25 struct { char *name; unsigned int count; } logtbl[MAXLOG_VALUE]; static LOG(e,f) char *f; { sii_log[sii_logpt++] = (e); if (sii_logpt == LOGSIZE) sii_logpt = 0; if ((e) < MAXLOG_VALUE) { logtbl[(e)].name = (f); logtbl[(e)].count++; } } sii_print_log(skip) int skip; { register int i, j; register unsigned char c; for (i = 0, j = sii_logpt; i < LOGSIZE; i++) { c = sii_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); } db_printf("\n"); return 0; } sii_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 */ struct cnt { unsigned int zeroes; unsigned int usage; unsigned int avg; unsigned int min; unsigned int max; }; static bump(counter, value) register struct cnt *counter; register unsigned int value; { register unsigned int n; if (value == 0) { counter->zeroes++; return; } n = counter->usage + 1; counter->usage = n; if (n == 0) { printf("{Counter at x%x overflowed with avg x%x}", counter, counter->avg); return; } else if (n == 1) counter->min = 0xffffffff; counter->avg = ((counter->avg * (n - 1)) + value) / n; if (counter->min > value) counter->min = value; if (counter->max < value) counter->max = value; } struct cnt s_cnt; #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. */ sii_probe(reg, ui) unsigned reg; struct bus_ctlr *ui; { int unit = ui->unit; sii_softc_t sii = &sii_softc_data[unit]; int target_id, i; scsi_softc_t *sc; register sii_padded_regmap_t *regs; spl_t s; boolean_t did_banner = FALSE; char *dma_ptr; static char *here = "sii_probe"; /* * We are only called if the chip is there, * but make sure anyways.. */ if (check_memory(reg, 0)) return 0; #ifdef MACH_KERNEL /* Mappable version side */ SII_probe(reg, ui); #endif /*MACH_KERNEL*/ /* * Initialize hw descriptor */ sii_softc[unit] = sii; sii->regs = (sii_padded_regmap_t *) (reg); sii->buff = (volatile char*) (reg + SII_OFFSET_RAM); queue_init(&sii->waiting_targets); sc = scsi_master_alloc(unit, sii); sii->sc = sc; sc->go = sii_go; sc->watchdog = scsi_watchdog; sc->probe = sii_probe_target; sii->wd.reset = sii_reset_scsibus; #ifdef MACH_KERNEL sc->max_dma_data = -1; /* unlimited */ #else sc->max_dma_data = scsi_per_target_virtual; #endif regs = sii->regs; /* * Clear out dma buffer */ blkclr(sii->buff, SII_RAM_SIZE); /* * Reset chip, fully. */ s = splbio(); sii_reset(regs, TRUE); /* * Our SCSI id on the bus. * The user can set this via the prom on pmaxen/3maxen. * If this changes it is easy to fix: make a default that * can be changed as boot arg. */ #ifdef unneeded regs->sii_id = (scsi_initiator_id[unit] & SII_ID_MASK)|SII_ID_IO; #endif sc->initiator_id = regs->sii_id & SII_ID_MASK; 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. */ for (target_id = 0, dma_ptr = (char*)sii->buff; target_id < 8; target_id++, dma_ptr += (PER_TGT_DMA_SIZE*2)) { register unsigned csr, dsr; register scsi_status_byte_t status; /* except of course ourselves */ if (target_id == sc->initiator_id) continue; regs->sii_sel_csr = target_id; wbflush(); /* select */ regs->sii_cmd = SII_CMD_SEL; wbflush(); /* wait for a selection timeout delay, and some more */ delay(251000); dsr = regs->sii_data_csr; csr = regs->sii_conn_csr; if ((csr & SII_CON_CON) == 0) { regs->sii_conn_csr = csr;/*wtc bits*/ /* abort sel in progress */ if (csr & SII_CON_SIP) { regs->sii_cmd = SII_CMD_DIS; wbflush(); csr = sii_wait(®s->sii_conn_csr, SII_CON_SCH,1); regs->sii_conn_csr = 0xffff;/*wtc bits */ regs->sii_data_csr = 0xffff; regs->sii_cmd = 0; wbflush(); } continue; } printf(",%s%d", did_banner++ ? " " : " target(s) at ", target_id); /* should be command phase here */ if (SCSI_PHASE(dsr) != SCSI_PHASE_CMD) panic(here); /* acknowledge state change */ SII_ACK(regs,csr,dsr,0); /* build command in (bogus) dma area */ { unsigned int *p = (unsigned int*) dma_ptr; p[0] = SCSI_CMD_TEST_UNIT_READY | (0 << 8); p[1] = 0 | (0 << 8); p[2] = 0 | (0 << 8); } /* set up dma xfer parameters */ regs->sii_dma_len = 6; regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); wbflush(); /* issue dma command */ SII_COMMAND(regs,csr,dsr,SII_CMD_XFER|SII_CMD_DMA); /* wait till done */ dsr = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); regs->sii_cmd &= ~(SII_CMD_XFER|SII_CMD_DMA); regs->sii_data_csr = SII_DTR_DONE;/* clear */ regs->sii_dma_len = 0; /* move on to status phase */ dsr = sii_wait(®s->sii_data_csr, SCSI_PHASE_STATUS,1); csr = regs->sii_conn_csr; SII_ACK(regs,csr,dsr,0); if (SCSI_PHASE(dsr) != SCSI_PHASE_STATUS) panic(here); /* get status byte */ dsr = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); csr = regs->sii_conn_csr; status.bits = regs->sii_data; if (status.st.scsi_status_code != SCSI_ST_GOOD) scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); /* get cmd_complete message */ SII_ACK(regs,csr,dsr,0); SII_COMMAND(regs,csr,dsr,SII_CMD_XFER); dsr = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); dsr = sii_wait(®s->sii_data_csr, SCSI_PHASE_MSG_IN,1); csr = regs->sii_conn_csr; SII_ACK(regs,csr,dsr,0); i = regs->sii_data; SII_COMMAND(regs,csr,dsr,SII_CMD_XFER); /* check disconnected, clear all intr bits */ csr = sii_wait(®s->sii_conn_csr, SII_CON_SCH,1); if (regs->sii_conn_csr & SII_CON_CON) panic(here); regs->sii_data_csr = 0xffff; regs->sii_conn_csr = 0xffff; regs->sii_cmd = 0; /* * Found a target */ sii->ntargets++; { register target_info_t *tgt; tgt = scsi_slave_alloc(sc->masterno, target_id, sii); tgt->dma_ptr = dma_ptr; tgt->cmd_ptr = decent_buffer[unit*8 + target_id]; #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 sii_probe_target(tgt, ior) target_info_t *tgt; io_req_t ior; { sii_softc_t sii = sii_softc[tgt->masterno]; boolean_t newlywed; int sii_probe_timeout(); newlywed = (tgt->cmd_ptr == 0); if (newlywed) { /* desc was allocated afresh */ char *dma_ptr = (char*)sii->buff; dma_ptr += (PER_TGT_DMA_SIZE * tgt->target_id)*2; tgt->dma_ptr = dma_ptr; tgt->cmd_ptr = decent_buffer[tgt->masterno*8 + tgt->target_id]; #ifdef MACH_KERNEL #else /*MACH_KERNEL*/ fdma_init(&tgt->fdma, scsi_per_target_virtual); #endif /*MACH_KERNEL*/ } /* Unfortunately, the SII chip does not have timeout support for selection */ timeout(sii_probe_timeout, tgt, hz); if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) return FALSE; untimeout(sii_probe_timeout, tgt); tgt->flags = TGT_ALIVE; return TRUE; } sii_probe_timeout(tgt) target_info_t *tgt; { sii_softc_t sii = (sii_softc_t)tgt->hw_state; register sii_padded_regmap_t *regs = sii->regs; int cs, ds; spl_t s; /* cancelled ? */ if (tgt->done != SCSI_RET_IN_PROGRESS) return; s = splbio(); /* Someone else might be using the bus (rare) */ switch (regs->sii_conn_csr & (SII_CON_LST|SII_CON_SIP)) { case SII_CON_SIP: /* We really timed out */ break; case SII_CON_SIP|SII_CON_LST: /* Someone else is (still) using the bus */ sii->wd.watchdog_state = SCSI_WD_ACTIVE; /* fall-through */ default: /* Did not get a chance to the bus yet */ timeout(sii_probe_timeout, tgt, hz); goto ret; } regs->sii_cmd = SII_CMD_DIS; wbflush(); regs->sii_csr |= SII_CSR_RSE; regs->sii_cmd = 0; wbflush(); sii->done = SCSI_RET_DEVICE_DOWN; cs = regs->sii_conn_csr; ds = regs->sii_data_csr; if (!sii_end(sii, cs, ds)) (void) sii_reconnect(sii, cs, ds); ret: splx(s); } static sii_wait(preg, until, complain) volatile unsigned short *preg; { int timeo = 1000000; while ((*preg & until) != until) { delay(1); if (!timeo--) { if (complain) { gimmeabreak(); printf("sii_wait TIMEO with x%x\n", *preg); } break; } } #ifdef DEBUG bump(&s_cnt, 1000000-timeo); #endif return *preg; } sii_reset(regs, quickly) register sii_padded_regmap_t *regs; boolean_t quickly; { int my_id; my_id = regs->sii_id & SII_ID_MASK; regs->sii_cmd = SII_CMD_RESET; wbflush(); delay(30); /* clear them all random bitsy */ regs->sii_conn_csr = SII_CON_SWA|SII_CON_SCH|SII_CON_BERR|SII_CON_RST; regs->sii_data_csr = SII_DTR_ATN|SII_DTR_DONE; regs->sii_id = my_id | SII_ID_IO; regs->sii_dma_ctrl = 0; /* asynch */ regs->sii_dma_len = 0; regs->sii_dma_adr_low = 0; regs->sii_dma_adr_hi = 0; regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|SII_CSR_HPM; /* later: SII_CSR_RSE */ regs->sii_diag_csr = SII_DIAG_PORT_ENB; wbflush(); if (quickly) return; /* * reset the scsi bus, the interrupt routine does the rest * or you can call sii_bus_reset(). */ regs->sii_cmd = SII_CMD_RST; } /* * Operational functions */ /* * Start a SCSI command on a target */ sii_go(tgt, cmd_count, in_count, cmd_only) target_info_t *tgt; boolean_t cmd_only; { sii_softc_t sii; register spl_t s; boolean_t disconn; script_t scp; boolean_t (*handler)(); LOG(1,"go"); sii = (sii_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*/ copyout_gap16(tgt->cmd_ptr, tgt->dma_ptr, cmd_count); if ((tgt->cur_cmd == SCSI_CMD_WRITE) || (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){ io_req_t ior = tgt->ior; register int len = ior->io_count; tgt->transient_state.out_count = len; if (len > PER_TGT_BUFF_SIZE) len = PER_TGT_BUFF_SIZE; copyout_gap16( ior->io_data, tgt->dma_ptr + (cmd_count<<1), len); tgt->transient_state.copy_count = len; /* avoid leaks */ if (len < tgt->block_size) { bzero_gap16(tgt->dma_ptr + ((cmd_count + len)<<1), len - tgt->block_size); len = tgt->block_size; tgt->transient_state.copy_count = len; } } else { tgt->transient_state.out_count = 0; tgt->transient_state.copy_count = 0; } tgt->transient_state.cmd_count = cmd_count; tgt->transient_state.isa_oddbb = FALSE; disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id); disconn = disconn && (sii->ntargets > 1); disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); /* * Setup target state */ tgt->done = SCSI_RET_IN_PROGRESS; handler = (disconn) ? sii_err_disconn : sii_err_generic; switch (tgt->cur_cmd) { case SCSI_CMD_READ: case SCSI_CMD_LONG_READ: LOG(0x13,"readop"); scp = sii_script_data_in; break; case SCSI_CMD_WRITE: case SCSI_CMD_LONG_WRITE: LOG(0x14,"writeop"); scp = sii_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 = sii_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 = sii_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 = sii_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 done already */ if (tgt->flags & TGT_DID_SYNCH) { scp = sii_script_cmd; } else { scp = sii_script_try_synch; tgt->flags |= TGT_TRY_SYNCH; } LOG(0x1c,"cmdop"); LOG(0x80+tgt->cur_cmd,0); break; default: LOG(0x1c,"cmdop"); LOG(0x80+tgt->cur_cmd,0); scp = sii_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 sii 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 (sii->wd.nactive++ == 0) sii->wd.watchdog_state = SCSI_WD_ACTIVE; if (sii->state & SII_STATE_BUSY) { /* * Queue up this target, note that this takes care * of proper FIFO scheduling of the scsi-bus. */ LOG(3,"enqueue"); LOG(0x80+tgt->target_id,0); enqueue_tail(&sii->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. */ sii->state |= SII_STATE_BUSY; sii->next_target = tgt; sii_attempt_selection(sii); /* * Note that we might still lose arbitration.. */ } splx(s); } sii_attempt_selection(sii) sii_softc_t sii; { target_info_t *tgt; register int out_count; sii_padded_regmap_t *regs; register int cmd; regs = sii->regs; tgt = sii->next_target; LOG(4,"select"); LOG(0x80+tgt->target_id,0); /* * Init bus state variables and set registers. * [They are intermixed to avoid wbflush()es] */ sii->active_target = tgt; out_count = tgt->transient_state.cmd_count; /* set dma pointer and counter */ regs->sii_dma_len = out_count; regs->sii_dma_adr_low = SII_DMADR_LO(tgt->dma_ptr); regs->sii_dma_adr_hi = SII_DMADR_HI(tgt->dma_ptr); sii->error_handler = tgt->transient_state.handler; regs->sii_sel_csr = tgt->target_id; sii->done = SCSI_RET_IN_PROGRESS; regs->sii_dma_ctrl = tgt->sync_offset; sii->cmd_count = out_count; /* if (regs->sii_conn_csr & (SII_CON_CON|SII_CON_DST))*/ if (regs->sii_sc1 & (SII_CS1_BSY|SII_CS1_SEL)) return; regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|SII_CSR_HPM; sii->script = tgt->transient_state.script; sii->in_count = 0; sii->out_count = 0; if (tgt->flags & TGT_DID_SYNCH) { if (tgt->transient_state.identify == 0xff) cmd = SII_CMD_SEL; else { cmd = SII_CMD_SEL | SII_CMD_ATN | SII_CMD_CON | SII_CMD_XFER | SCSI_PHASE_MSG_OUT; /* chain select and message out */ /*??*/ regs->sii_dma_1st_byte = tgt->transient_state.identify; } } else if (tgt->flags & TGT_TRY_SYNCH) cmd = SII_CMD_SEL | SII_CMD_ATN; else cmd = SII_CMD_SEL; /* if (regs->sii_conn_csr & (SII_CON_CON|SII_CON_DST)) { */ if (regs->sii_sc1 & (SII_CS1_BSY|SII_CS1_SEL)) { /* let the reconnection attempt proceed */ regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE| SII_CSR_HPM|SII_CSR_RSE; sii->script = 0; LOG(0x8c,0); } else { regs->sii_cmd = cmd; wbflush(); } } /* * Interrupt routine * Take interrupts from the chip * * Implementation: * Move along the current command's script if * all is well, invoke error handler if not. */ boolean_t sii_inside_sii_intr = FALSE; sii_intr(unit,spllevel) { register sii_softc_t sii; register script_t scp; register int cs, ds; register sii_padded_regmap_t *regs; boolean_t try_match; #ifdef MACH_KERNEL extern boolean_t rz_use_mapped_interface; if (rz_use_mapped_interface) return SII_intr(unit,spllevel); #endif /*MACH_KERNEL*/ /* interrupt code is NOT reentrant */ if (sii_inside_sii_intr) { LOG(0x22,"!!attempted to reenter sii_intr!!"); return; } sii_inside_sii_intr = TRUE; LOG(5,"\n\tintr"); sii = sii_softc[unit]; /* collect status information */ regs = sii->regs; cs = regs->sii_conn_csr; ds = regs->sii_data_csr; TR(cs); TR(ds); TR(regs->sii_cmd); TRCHECK; if (cs & SII_CON_RST){ sii_bus_reset(sii); goto getout; } /* we got an interrupt allright */ if (sii->active_target) sii->wd.watchdog_state = SCSI_WD_ACTIVE; /* rid of DONEs */ if (ds & SII_DTR_DONE) { regs->sii_data_csr = SII_DTR_DONE; LOG(0x1e,"done"); ds = regs->sii_data_csr; cs = regs->sii_conn_csr; } /* drop spurious calls, note that sometimes * ds and cs get out-of-sync */ if (((cs & SII_CON_CI) | (ds & SII_DTR_DI)) == 0) { LOG(2,"SPURIOUS"); goto getout; } /* clear interrupt flags */ regs->sii_conn_csr = cs; regs->sii_data_csr = cs; /* drop priority */ splx(spllevel); if ((sii->state & SII_STATE_TARGET) || (cs & SII_CON_TGT)) { sii_target_intr(sii,cs,ds); goto getout; } scp = sii->script; /* check who got the bus */ if ((scp == 0) || (cs & SII_CON_LST)) { if (cs & SII_CON_DST) { sii_reconnect(sii, cs, ds); goto getout; } LOG(0x12,"no-script"); goto getout; } if (SCRIPT_MATCH(cs,ds) != scp->condition) { if (try_match = (*sii->error_handler)(sii, cs, ds)) { cs = regs->sii_conn_csr; ds = regs->sii_data_csr; } } else try_match = TRUE; /* might have been side effected */ scp = sii->script; if (try_match && (SCRIPT_MATCH(cs,ds) == scp->condition)) { /* * Perform the appropriate operation, * then proceed */ if ((*scp->action)(sii, cs, ds)) { /* might have been side effected */ scp = sii->script; sii->script = scp + 1; regs->sii_cmd = scp->command; wbflush(); } } getout: sii_inside_sii_intr = FALSE; } sii_target_intr(sii) register sii_softc_t sii; { panic("SII: TARGET MODE !!!\n"); } /* * All the many little things that the interrupt * routine might switch to */ boolean_t sii_script_true(sii, cs, ds) register sii_softc_t sii; { SII_COMMAND(sii->regs,cs,ds,SII_CON_CON/*sanity*/); LOG(7,"nop"); return TRUE; } boolean_t sii_end_transaction( sii, cs, ds) register sii_softc_t sii; { register sii_padded_regmap_t *regs = sii->regs; SII_COMMAND(sii->regs,cs,ds,0); LOG(0x1f,"end_t"); regs->sii_csr &= ~SII_CSR_RSE; /* is the fifo really clean here ? */ ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); if (regs->sii_data != SCSI_COMMAND_COMPLETE) printf("{T%x}", regs->sii_data); regs->sii_cmd = SII_CMD_XFER | SII_CON_CON | SCSI_PHASE_MSG_IN | (cs & SII_CON_DST); wbflush(); ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); regs->sii_data_csr = SII_DTR_DONE; regs->sii_cmd = 0/*SII_PHASE_DISC*/; wbflush(); cs = regs->sii_conn_csr; if ((cs & SII_CON_SCH) == 0) cs = sii_wait(®s->sii_conn_csr, SII_CON_SCH,1); regs->sii_conn_csr = SII_CON_SCH; regs->sii_csr |= SII_CSR_RSE; cs = regs->sii_conn_csr; if (!sii_end(sii, cs, ds)) (void) sii_reconnect(sii, cs, ds); return FALSE; } boolean_t sii_end( sii, cs, ds) register sii_softc_t sii; { register target_info_t *tgt; register io_req_t ior; register sii_padded_regmap_t *regs = sii->regs; LOG(6,"end"); tgt = sii->active_target; if ((tgt->done = sii->done) == SCSI_RET_IN_PROGRESS) tgt->done = SCSI_RET_SUCCESS; sii->script = 0; if (sii->wd.nactive-- == 1) sii->wd.watchdog_state = SCSI_WD_INACTIVE; /* check reconnection not pending */ cs = regs->sii_conn_csr; if ((cs & SII_CON_DST) == 0) sii_release_bus(sii); else { sii->active_target = 0; /* sii->state &= ~SII_STATE_BUSY; later */ } if (ior = tgt->ior) { LOG(0xA,"ops->restart"); #ifdef MACH_KERNEL #else /*MACH_KERNEL*/ fdma_unmap(&tgt->fdma, ior); #endif /*MACH_KERNEL*/ (*tgt->dev_ops->restart)(tgt, TRUE); if (cs & SII_CON_DST) sii->state &= ~SII_STATE_BUSY; } return ((cs & SII_CON_DST) == 0); } boolean_t sii_release_bus(sii) register sii_softc_t sii; { boolean_t ret = FALSE; LOG(9,"release"); sii->script = 0; if (sii->state & SII_STATE_COLLISION) { LOG(0xB,"collided"); sii->state &= ~SII_STATE_COLLISION; sii_attempt_selection(sii); } else if (queue_empty(&sii->waiting_targets)) { sii->state &= ~SII_STATE_BUSY; sii->active_target = 0; ret = TRUE; } else { LOG(0xC,"dequeue"); sii->next_target = (target_info_t *) dequeue_head(&sii->waiting_targets); sii_attempt_selection(sii); } return ret; } boolean_t sii_get_status( sii, cs, ds) register sii_softc_t sii; { register sii_padded_regmap_t *regs = sii->regs; register scsi2_status_byte_t status; register target_info_t *tgt; unsigned int len; unsigned short cmd; LOG(0xD,"get_status"); TRWRAP; sii->state &= ~SII_STATE_DMA_IN; tgt = sii->active_target; sii->error_handler = tgt->transient_state.handler; if (len = sii->in_count) { if ((tgt->cur_cmd != SCSI_CMD_READ) && (tgt->cur_cmd != SCSI_CMD_LONG_READ)){ len -= regs->sii_dma_len; copyin_gap16(tgt->dma_ptr, tgt->cmd_ptr, len); if (len & 0x1) /* odd byte, left in silo */ tgt->cmd_ptr[len - 1] = regs->sii_data; } else { if (regs->sii_dma_len) { #if 0 this is incorrect and besides.. tgt->ior->io_residual = regs->sii_dma_len; #endif len -= regs->sii_dma_len; } careful_copyin_gap16( tgt, tgt->transient_state.dma_offset, len, ds & SII_DTR_OBB, regs->sii_dma_1st_byte); } sii->in_count = 0; } len = regs->sii_dma_len; regs->sii_dma_len = 0;/*later?*/ /* if dma is still in progress we have to quiet it down */ cmd = regs->sii_cmd; if (cmd & SII_CMD_DMA) { regs->sii_cmd = cmd & ~(SII_CMD_DMA|SII_CMD_XFER); wbflush(); /* DONE might NOT pop up. Sigh. */ delay(10); regs->sii_data_csr = regs->sii_data_csr; } regs->sii_cmd = SCSI_PHASE_STATUS|SII_CON_CON|(cs & SII_CON_DST); wbflush(); ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); status.bits = regs->sii_data; if (status.st.scsi_status_code != SCSI_ST_GOOD) { scsi_error(sii->active_target, SCSI_ERR_STATUS, status.bits, 0); sii->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; } else sii->done = SCSI_RET_SUCCESS; return TRUE; } boolean_t sii_dma_in( sii, cs, ds) register sii_softc_t sii; { register target_info_t *tgt; register sii_padded_regmap_t *regs = sii->regs; char *dma_ptr; register int count; boolean_t advance_script = TRUE; SII_COMMAND(regs,cs,ds,0); LOG(0xE,"dma_in"); tgt = sii->active_target; sii->error_handler = tgt->transient_state.handler; sii->state |= SII_STATE_DMA_IN; if (sii->in_count == 0) { /* * Got nothing yet: either just sent the command * or just reconnected */ register int avail; if (tgt->transient_state.isa_oddbb) { regs->sii_dma_1st_byte = tgt->transient_state.oddbb; tgt->transient_state.isa_oddbb = FALSE; } count = tgt->transient_state.in_count; count = u_min(count, (SII_DMA_COUNT_MASK+1)); 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. * Also, take care of bogus interrupts */ register int offset, xferred; unsigned char obb = regs->sii_data; xferred = sii->in_count - regs->sii_dma_len; 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, (SII_DMA_COUNT_MASK+1)); 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); } /* get some more */ dma_ptr = tgt->dma_ptr + (tgt->transient_state.dma_offset << 1); sii->in_count = count; regs->sii_dma_len = count; regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); wbflush(); regs->sii_cmd = sii->script->command; wbflush(); /* copy what we got */ careful_copyin_gap16( tgt, offset, xferred, ds & SII_DTR_OBB, obb); /* last chunk ? */ if (count == tgt->transient_state.in_count) { sii->script++; } return FALSE; } quickie: sii->in_count = count; dma_ptr = tgt->dma_ptr + (tgt->transient_state.dma_offset << 1); regs->sii_dma_len = count; regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); wbflush(); if (!advance_script) { regs->sii_cmd = sii->script->command; wbflush(); } 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 */ boolean_t sii_dma_out( sii, cs, ds) register sii_softc_t sii; { register sii_padded_regmap_t *regs = sii->regs; register char *dma_ptr; register target_info_t *tgt; boolean_t advance_script = TRUE; int count = sii->out_count; SII_COMMAND(regs,cs,ds,0); LOG(0xF,"dma_out"); tgt = sii->active_target; sii->error_handler = tgt->transient_state.handler; sii->state &= ~SII_STATE_DMA_IN; if (sii->out_count == 0) { /* * Nothing committed: either just sent the * command or reconnected */ register int remains; count = tgt->transient_state.out_count; count = u_min(count, (SII_DMA_COUNT_MASK+1)); 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; xferred = sii->out_count - regs->sii_dma_len; 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, (SII_DMA_COUNT_MASK+1)); 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) << 1); sii->out_count = count; regs->sii_dma_len = count; regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); wbflush(); regs->sii_cmd = sii->script->command; /* copy some more data */ careful_copyout_gap16(tgt, offset, xferred); return FALSE; } quickie: sii->out_count = count; dma_ptr = tgt->dma_ptr + ((tgt->transient_state.cmd_count + tgt->transient_state.dma_offset) << 1); regs->sii_dma_len = count; regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); wbflush(); if (!advance_script) { regs->sii_cmd = sii->script->command; wbflush(); } return advance_script; } /* disconnect-reconnect ops */ /* get the message in via dma */ boolean_t sii_msg_in(sii, cs, ds) register sii_softc_t sii; register unsigned char cs, ds; { register target_info_t *tgt; char *dma_ptr; register sii_padded_regmap_t *regs = sii->regs; LOG(0x15,"msg_in"); tgt = sii->active_target; dma_ptr = tgt->dma_ptr; /* We would clobber the data for READs */ if (sii->state & SII_STATE_DMA_IN) { register int offset; offset = tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; if (offset & 1) offset++; dma_ptr += (offset << 1); } regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); /* We only really expect two bytes */ regs->sii_dma_len = sizeof(scsi_command_group_0); wbflush(); return TRUE; } /* check the message is indeed a DISCONNECT */ boolean_t sii_disconnect(sii, cs, ds) register sii_softc_t sii; register unsigned char cs, ds; { register target_info_t *tgt; register int len; boolean_t ok = FALSE; unsigned int dmsg = 0; tgt = sii->active_target; len = sizeof(scsi_command_group_0) - sii->regs->sii_dma_len; PRINT(("{G%d}",len)); /* if (len == 0) ok = FALSE; */ if (len == 1) { dmsg = sii->regs->sii_dma_1st_byte; ok = (dmsg == SCSI_DISCONNECT); } else if (len == 2) { register char *msgs; register unsigned int offset; register sii_padded_regmap_t *regs = sii->regs; /* wherever it was, take it from there */ offset = regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16); msgs = (char*)sii->buff + (offset << 1); dmsg = *((unsigned short *)msgs); /* A SDP message preceeds it in non-completed READs */ ok = (((dmsg & 0xff) == SCSI_DISCONNECT) || (dmsg == ((SCSI_DISCONNECT<<8)|SCSI_SAVE_DATA_POINTER))); } if (!ok) printf("[tgt %d bad msg (%d): %x]", tgt->target_id, len, dmsg); return TRUE; } /* save all relevant data, free the BUS */ boolean_t sii_disconnected(sii, cs, ds) register sii_softc_t sii; register unsigned char cs, ds; { register target_info_t *tgt; SII_COMMAND(sii->regs,cs,ds,0); sii->regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE| SII_CSR_HPM|SII_CSR_RSE; LOG(0x16,"disconnected"); sii_disconnect(sii,cs,ds); tgt = sii->active_target; tgt->flags |= TGT_DISCONNECTED; tgt->transient_state.handler = sii->error_handler; /* the rest has been saved in sii_err_disconn() */ PRINT(("{D%d}", tgt->target_id)); sii_release_bus(sii); return FALSE; } /* get reconnect message, restore BUS */ boolean_t sii_reconnect(sii, cs, ds) register sii_softc_t sii; register unsigned char cs, ds; { register target_info_t *tgt; sii_padded_regmap_t *regs; int id; regs = sii->regs; regs->sii_conn_csr = SII_CON_SCH; regs->sii_cmd = SII_CON_CON|SII_CON_DST|SCSI_PHASE_MSG_IN; wbflush(); LOG(0x17,"reconnect"); /* * See if this reconnection collided with a selection attempt */ if (sii->state & SII_STATE_BUSY) sii->state |= SII_STATE_COLLISION; sii->state |= SII_STATE_BUSY; cs = regs->sii_conn_csr; /* tk50s are slow */ if ((cs & SII_CON_CON) == 0) cs = sii_wait(®s->sii_conn_csr, SII_CON_CON,1); /* ?? */ if (regs->sii_conn_csr & SII_CON_BERR) regs->sii_conn_csr = SII_CON_BERR; if ((ds & SII_DTR_IBF) == 0) ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); if (regs->sii_data != SCSI_IDENTIFY) printf("{I%x %x}", regs->sii_data, regs->sii_dma_1st_byte); /* find tgt: id is in sii_destat */ id = regs->sii_destat; tgt = sii->sc->target[id]; if (id > 7 || tgt == 0) panic("sii_reconnect"); PRINT(("{R%d}", id)); if (sii->state & SII_STATE_COLLISION) PRINT(("[B %d-%d]", sii->active_target->target_id, id)); LOG(0x80+id,0); sii->active_target = tgt; tgt->flags &= ~TGT_DISCONNECTED; /* synch params */ regs->sii_dma_ctrl = tgt->sync_offset; regs->sii_dma_len = 0; sii->script = tgt->transient_state.script; sii->error_handler = sii_err_rdp; sii->in_count = 0; sii->out_count = 0; regs->sii_cmd = SII_CMD_XFER|SII_CMD_CON|SII_CMD_DST|SCSI_PHASE_MSG_IN; wbflush(); (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); regs->sii_data_csr = SII_DTR_DONE; return TRUE; } /* do the synch negotiation */ boolean_t sii_dosynch( sii, cs, ds) register sii_softc_t sii; { /* * Phase is MSG_OUT here, cmd has not been xferred */ int *p, len; unsigned short dmalo, dmahi, dmalen; register target_info_t *tgt; register sii_padded_regmap_t *regs = sii->regs; unsigned char off; regs->sii_cmd = SCSI_PHASE_MSG_OUT|SII_CMD_ATN|SII_CON_CON; wbflush(); LOG(0x11,"dosync"); tgt = sii->active_target; tgt->flags |= TGT_DID_SYNCH; /* only one chance */ tgt->flags &= ~TGT_TRY_SYNCH; p = (int*) (tgt->dma_ptr + (((regs->sii_dma_len<<1) + 2) & ~3)); p[0] = SCSI_IDENTIFY | (SCSI_EXTENDED_MESSAGE<<8); p[1] = 3 | (SCSI_SYNC_XFER_REQUEST<<8); if (BGET(scsi_no_synchronous_xfer,tgt->masterno,tgt->target_id)) off = 0; else off = sii_max_offset; /* but we'll ship "off" manually */ p[2] = sii_to_scsi_period(sii_min_period) |(off << 8); dmalen = regs->sii_dma_len; dmalo = regs->sii_dma_adr_low; dmahi = regs->sii_dma_adr_hi; regs->sii_dma_len = sizeof(scsi_synch_xfer_req_t) /* + 1 */; regs->sii_dma_adr_low = SII_DMADR_LO(p); regs->sii_dma_adr_hi = SII_DMADR_HI(p); wbflush(); regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SII_CMD_ATN| SII_CON_CON|SCSI_PHASE_MSG_OUT; wbflush(); /* wait for either DONE or MIS */ ds = sii_wait(®s->sii_data_csr, SII_DTR_DI,1); /* TK50s do not like xtended messages */ /* and some others just ignore the standard */ if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_OUT) { /* disentangle FIFO */ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT; ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); if (SCSI_PHASE(ds) == SCSI_PHASE_MSG_IN) goto msgin; goto got_answer; } /* ack and stop dma */ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT|SII_CMD_ATN; wbflush(); ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); regs->sii_data_csr = SII_DTR_DONE; wbflush(); /* last byte of message */ regs->sii_data = off; wbflush(); regs->sii_cmd = SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_MSG_OUT; wbflush(); /* Race here: who will interrupt first, the DMA controller or the status watching machine ? */ delay(1000); regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT; wbflush(); ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); regs->sii_data_csr = SII_DTR_DONE; /* The standard sez there nothing else the target can do but.. */ ds = sii_wait(®s->sii_data_csr, SCSI_PHASE_MSG_IN,0); /* Of course, what are standards for ? */ if (SCSI_PHASE(ds) == SCSI_PHASE_CMD) goto cmdp; msgin: /* ack */ regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_IN; wbflush(); /* set up dma to receive answer */ regs->sii_dma_adr_low = SII_DMADR_LO(p); regs->sii_dma_adr_hi = SII_DMADR_HI(p); regs->sii_dma_len = sizeof(scsi_synch_xfer_req_t); wbflush(); regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_MSG_IN; wbflush(); /* wait for the answer, and look at it */ ds = sii_wait(®s->sii_data_csr, SII_DTR_MIS,1); regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_IN; wbflush(); ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); got_answer: /* do not cancel the phase mismatch */ regs->sii_data_csr = SII_DTR_DONE; if (regs->sii_dma_len || ((p[0] & 0xff) != SCSI_EXTENDED_MESSAGE)) { /* did not like it */ printf(" did not like SYNCH xfer "); } else { /* will do synch */ tgt->sync_period = scsi_period_to_sii((p[1]>>8)&0xff); tgt->sync_offset = regs->sii_data; /* odd xfer, in silo */ /* sanity */ if (tgt->sync_offset > sii_max_offset) tgt->sync_offset = sii_max_offset; regs->sii_dma_ctrl = tgt->sync_offset; } cmdp: /* phase should be command now */ regs->sii_dma_len = dmalen; regs->sii_dma_adr_low = dmalo; regs->sii_dma_adr_hi = dmahi; wbflush(); /* continue with simple command script */ sii->error_handler = sii_err_generic; sii->script = (tgt->cur_cmd == SCSI_CMD_INQUIRY) ? sii_script_data_in : sii_script_cmd; if (SCSI_PHASE(ds) == SCSI_PHASE_CMD ) return TRUE; sii->script++; if (SCSI_PHASE(ds) == SCSI_PHASE_STATUS ) return TRUE; sii->script++; /* msgin? */ sii->script++; if (SCSI_PHASE(ds) == SII_PHASE_DISC) return TRUE; gimmeabreak(); panic("sii_dosynch"); return FALSE; } /* * The bus was reset */ sii_bus_reset(sii) register sii_softc_t sii; { register sii_padded_regmap_t *regs = sii->regs; LOG(0x21,"bus_reset"); /* * Clear interrupt bits */ regs->sii_conn_csr = 0xffff; regs->sii_data_csr = 0xffff; /* * Clear bus descriptor */ sii->script = 0; sii->error_handler = 0; sii->active_target = 0; sii->next_target = 0; sii->state = 0; queue_init(&sii->waiting_targets); sii->wd.nactive = 0; sii_reset(regs, TRUE); log(LOG_KERN, "sii: (%d) bus reset ", ++sii->wd.reset_count); delay(scsi_delay_after_reset); /* some targets take long to reset */ if (sii->sc == 0) /* sanity */ return; sii_inside_sii_intr = FALSE; scsi_bus_was_reset(sii->sc); } /* * Error handlers */ /* * Generic, default handler */ boolean_t sii_err_generic(sii, cs, ds) register sii_softc_t sii; { register int cond = sii->script->condition; LOG(0x10,"err_generic"); /* * Note to DEC hardware people. * Dropping the notion of interrupting on * DMA completions (or at least make it optional) * would save TWO interrupts out of the SEVEN that * are currently requested for a non-disconnecting * READ or WRITE operation. */ if (ds & SII_DTR_DONE) return TRUE; /* this is a band-aid */ if ((SCSI_PHASE(cond) == SII_PHASE_DISC) && (cs & SII_CON_SCH)) { ds &= ~7; ds |= SII_PHASE_DISC; (void) (*sii->script->action)(sii,cs,ds); return FALSE; } /* TK50s are slow to connect, forgive em */ if ((SCSI_PHASE(ds) == SCSI_PHASE_MSG_OUT) || (SCSI_PHASE(cond) == SCSI_PHASE_MSG_OUT)) return TRUE; if ((SCSI_PHASE(cond) == SCSI_PHASE_CMD) && ((SCSI_PHASE(ds) == 0) || (SCSI_PHASE(ds) == 4) || (SCSI_PHASE(ds) == 5))) return TRUE; /* transition to status ? */ if (SCSI_PHASE(ds) == SCSI_PHASE_STATUS) return sii_err_to_status(sii, cs, ds); return sii_err_phase_mismatch(sii,cs,ds); } /* * Handle generic errors that are reported as * an unexpected change to STATUS phase */ sii_err_to_status(sii, cs, ds) register sii_softc_t sii; { script_t scp = sii->script; LOG(0x20,"err_tostatus"); while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS) scp++; sii->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. */ sii->done = SCSI_RET_NEED_SENSE; #endif return TRUE; } /* * Watch for a disconnection */ boolean_t sii_err_disconn(sii, cs, ds) register sii_softc_t sii; register unsigned cs, ds; { register sii_padded_regmap_t *regs; register target_info_t *tgt; int count; int from; unsigned char obb; int delayed_copy = 0; LOG(0x18,"err_disconn"); if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_IN) return sii_err_generic(sii, cs, ds); regs = sii->regs; if ((regs->sii_cmd & (SII_CMD_DMA|SII_CMD_XFER)) == (SII_CMD_DMA|SII_CMD_XFER)) { /* stop dma and wait */ regs->sii_cmd &= ~(SII_CMD_DMA|SII_CMD_XFER); (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); /* later: regs->sii_data_csr = SII_DTR_DONE; */ } SII_COMMAND(regs,cs,ds,0); tgt = sii->active_target; switch (SCSI_PHASE(sii->script->condition)) { case SCSI_PHASE_DATAO: LOG(0x1b,"+DATAO"); if (sii->out_count) { register int xferred, offset; xferred = sii->out_count - regs->sii_dma_len; 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; delayed_copy = 1; from = offset; count = xferred; } tgt->transient_state.script = sii_script_restart_data_out; break; case SCSI_PHASE_DATAI: LOG(0x19,"+DATAI"); if (sii->in_count) { register int offset, xferred; obb = regs->sii_dma_1st_byte; xferred = sii->in_count - regs->sii_dma_len; assert(xferred > 0); if (ds & SII_DTR_OBB) { tgt->transient_state.isa_oddbb = TRUE; tgt->transient_state.oddbb = obb; } 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 */ delayed_copy = 2; from = offset; count = xferred; } tgt->transient_state.script = sii_script_restart_data_in; break; case SCSI_PHASE_STATUS: /* will have to restart dma */ if (count = regs->sii_dma_len) { (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); regs->sii_data_csr = SII_DTR_DONE; } if (sii->state & SII_STATE_DMA_IN) { register int offset, xferred; obb = regs->sii_dma_1st_byte; LOG(0x1a,"+STATUS+R"); xferred = sii->in_count - count; assert(xferred > 0); if (ds & SII_DTR_OBB) { tgt->transient_state.isa_oddbb = TRUE; tgt->transient_state.oddbb = obb; } 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 */ delayed_copy = 2; from = offset; count = xferred; tgt->transient_state.script = sii_script_restart_data_in; if (tgt->transient_state.in_count == 0) tgt->transient_state.script++; } else { LOG(0x1d,"+STATUS+W"); if ((count == 0) && (tgt->transient_state.out_count == sii->out_count)) { /* all done */ tgt->transient_state.script = &sii_script_restart_data_out[1]; tgt->transient_state.out_count = 0; } else { register int xferred, offset; /* how much we xferred */ xferred = sii->out_count - count; 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; delayed_copy = 1; from = offset; count = xferred; tgt->transient_state.script = sii_script_restart_data_out; } sii->out_count = 0; } break; case SII_PHASE_DISC: /* sometimes disconnects and phase remains */ return sii_err_generic(sii, cs, ds); default: gimmeabreak(); } regs->sii_csr &= ~SII_CSR_RSE; sii_msg_in(sii,cs,ds); sii->script = sii_script_disconnect; regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SCSI_PHASE_MSG_IN| SII_CON_CON|(regs->sii_conn_csr & SII_CON_DST); wbflush(); if (delayed_copy == 2) careful_copyin_gap16( tgt, from, count, ds & SII_DTR_OBB, obb); else if (delayed_copy == 1) careful_copyout_gap16( tgt, from, count); return FALSE; } /* * Suppose someone reads the specs as they read the Bible. * They would send these unnecessary restore-pointer msgs * in reconnect phases. If this was a SCSI-2 modify-pointer * I could understand, but. Oh well. */ sii_err_rdp(sii, cs, ds) register sii_softc_t sii; { register sii_padded_regmap_t *regs; register target_info_t *tgt; LOG(0x24,"err_drp"); /* One chance */ sii->error_handler = sii->active_target->transient_state.handler; if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_IN) return sii_err_generic(sii, cs, ds); regs = sii->regs; if ((ds & SII_DTR_IBF) == 0) ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); if (regs->sii_data != SCSI_RESTORE_POINTERS) return sii_err_disconn(sii, cs, ds); regs->sii_cmd = SII_CMD_XFER|SII_CMD_CON|SII_CMD_DST|SCSI_PHASE_MSG_IN; wbflush(); (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); regs->sii_data_csr = SII_DTR_DONE; return FALSE; } /* * Handle strange, yet unexplained interrupts and error * situations which eventually I will be old and wise * enough to deal with properly with preventive care. */ sii_err_phase_mismatch(sii, cs, ds) register sii_softc_t sii; { register sii_padded_regmap_t *regs = sii->regs; register int match; LOG(0x23,"err_mismatch"); match = SCSI_PHASE(sii->script->condition); /* dmain interrupted */ if ((match == SCSI_PHASE_STATUS) && (SCSI_PHASE(ds) == SCSI_PHASE_DATAI)) { register int xferred; register char *p; if (regs->sii_dma_len <= 1) { /*if (scsi_debug)*/ printf("[DMAINZERO %x %x %x]", cs, ds, regs->sii_dma_len); if (regs->sii_dma_len == 0) { regs->sii_dma_len = sii->in_count; wbflush(); regs->sii_cmd = sii->script[-1].command; } return FALSE; } /* This happens when you do not "init" the prom and the fifo is screwed up */ xferred = sii->in_count - regs->sii_dma_len; p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) ); p += xferred; if (scsi_debug) printf("[DMAIN %x %x %x]", cs, ds, xferred); /* odd bytes are not xferred */ if (((unsigned)p) & 0x1){ register short *oddb; oddb = (short*)(sii->buff) + ((unsigned)p-1);/*shifts*/ *oddb = regs->sii_dma_1st_byte; } regs->sii_dma_adr_low = ((unsigned)p); regs->sii_dma_adr_hi = ((unsigned)p) << 16; wbflush(); regs->sii_cmd = sii->script[-1].command; wbflush(); return FALSE; } else /* dmaout interrupted */ if ((match == SCSI_PHASE_STATUS) && (SCSI_PHASE(ds) == SCSI_PHASE_DATAO)) { register int xferred; register char *p; if (regs->sii_dma_len <= 1) { /*if (scsi_debug)*/ printf("[DMAOUTZERO %x %x %x]", cs, ds, regs->sii_dma_len); gimmeabreak(); if (regs->sii_dma_len == 0) { regs->sii_dma_len = sii->out_count; wbflush(); regs->sii_cmd = sii->script[-1].command; } return FALSE; } xferred = sii->out_count - regs->sii_dma_len; /*if (scsi_debug)*/ printf("[DMAOUT %x %x %x %x]", cs, ds, regs->sii_dma_len, sii->out_count); sii->out_count -= xferred; p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) ); p += xferred; regs->sii_dma_adr_low = ((unsigned)p); regs->sii_dma_adr_hi = ((unsigned)p) << 16; wbflush(); regs->sii_cmd = sii->script[-1].command; wbflush(); return FALSE; } #if 1 /* ?? */ /* stuck in cmd phase */ else if ((SCSI_PHASE(ds) == SCSI_PHASE_CMD) && ((match == SCSI_PHASE_DATAI) || (match == SCSI_PHASE_DATAO))) { /*if (scsi_debug)*/ printf("[CMD %x %x %x %x]", cs, ds, sii->cmd_count, regs->sii_dma_len); if (regs->sii_dma_len != 0) { /* ouch, this hurts */ register int xferred; register char *p; xferred = sii->cmd_count - regs->sii_dma_len; sii->cmd_count -= xferred; p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) ); p += xferred; regs->sii_dma_adr_low = ((unsigned)p); regs->sii_dma_adr_hi = ((unsigned)p) << 16; wbflush(); regs->sii_cmd = 0x8842; wbflush(); return FALSE;; } SII_ACK(regs,cs,ds,0/*match*/); wbflush(); return FALSE;; } #endif else { printf("{D%x %x}", cs, ds); /* if (scsi_debug)*/ gimmeabreak(); } return FALSE; } /* * Watchdog * * There are two ways in which I have seen the chip * get stuck: a target never reconnected, or the * selection deadlocked. Both cases involved a tk50, * but elsewhere it showed up with hitachi disks too. */ sii_reset_scsibus(sii) register sii_softc_t sii; { register target_info_t *tgt = sii->active_target; register sii_padded_regmap_t *regs = sii->regs; /* see if SIP still --> device down or non-existant */ if ((regs->sii_conn_csr & (SII_CON_LST|SII_CON_SIP)) == SII_CON_SIP){ if (tgt) { log(LOG_KERN, "Target %d went offline\n", tgt->target_id); tgt->flags = 0; return sii_probe_timeout(tgt); } /* else fall through */ } if (tgt) 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, sii->in_count, sii->out_count, sii->regs->sii_dma_len); sii->regs->sii_cmd = SII_CMD_RST; delay(25); } /* * Copy routines that avoid odd pointers */ boolean_t nocopyin = FALSE; careful_copyin_gap16(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->dma_ptr + (offset << 1); to = tgt->ior->io_data + count; tgt->transient_state.copy_count = count + len; if (count & 1) { from -= (1 << 1); to -= 1; len += 1; } if (nocopyin) return;/*timing*/ copyin_gap16( from, to, len); /* check for last, poor little odd byte */ if (isaobb) { to += len; to[-1] = obb; } } careful_copyout_gap16( 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->dma_ptr + (offset << 1); /* the scsi buffer acts weirdo at times */ if ((olen=len) & 1) { p = tgt->dma_ptr + ((offset + olen - 1)<<1); c = (*(unsigned short*)p) >> 8;/*!MSF*/ } if (count & 1) { from -= 1; to -= (1 << 1); len += 1; } count = copyout_gap16(from, to, len); /* the scsi buffer acts weirdo at times */ if (olen & 1) { unsigned char cv; cv = (*(unsigned short*)p) >> 8;/*!MSF*/ if (c != cv) { /* * Scott Fahlman would say * "Use a big plier!" */ unsigned short s; volatile unsigned short *pp; pp = (volatile unsigned short*)p; s = (c << 8) | (from[len-1] & 0xff); do { *pp = s; } while (*pp != s); } } } } #endif NSII > 0