/* * 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_33C93_hdw.h * Author: Alessandro Forin, Carnegie Mellon University * Date: 8/91 * * Bottom layer of the SCSI driver: chip-dependent functions * * This file contains the code that is specific to the WD/AMD 33C93 * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start * operation, and interrupt routine. */ #if 0 DISCLAIMER: THIS DOES NOT EVEN COMPILE YET, it went in by mistake. Code that probably makes some sense is from here to "TILL HERE" /* * 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 NSBIC > 0 #include #ifdef IRIS #define PAD(n) char n[3] /* or whatever */ #define SBIC_MUX_ADDRESSING /* comment out if wrong */ #define SBIC_CLOCK_FREQUENCY 20 /* FIXME FIXME FIXME */ #define SBIC_MACHINE_DMA_MODE SBIC_CTL_DMA /* FIXME FIXME FIXME */ #define SBIC_SET_RST_ADDR /*SCSI_INIT_ADDR*/ #define SBIC_CLR_RST_ADDR /*SCSI_RDY_ADDR*/ #define SBIC_MACHINE_RESET_SCSIBUS(regs,per) \ { int temp; \ temp = *(volatile unsigned int *)SBIC_SET_RST_ADDR; \ delay(per); \ temp = *(volatile unsigned int *)SBIC_CLR_RST_ADDR; \ } #endif #include /* spl definitions */ #include #include #include #include #include #include #include #include #include /* * Spell out all combinations of padded/nopadded and mux/nomux */ #ifdef PAD typedef struct { volatile unsigned char sbic_myid; /* rw: My SCSI id */ /*#define sbic_cdbsize sbic_myid /* w : size of CDB */ PAD(pad0) volatile unsigned char sbic_control; /* rw: Control register */ PAD(pad1) volatile unsigned char sbic_timeo; /* rw: Timeout period */ PAD(pad2) volatile unsigned char sbic_cdb1; /* rw: CDB, 1st byte */ PAD(pad3) volatile unsigned char sbic_cdb2; /* rw: CDB, 2nd byte */ PAD(pad4) volatile unsigned char sbic_cdb3; /* rw: CDB, 3rd byte */ PAD(pad5) volatile unsigned char sbic_cdb4; /* rw: CDB, 4th byte */ PAD(pad6) volatile unsigned char sbic_cdb5; /* rw: CDB, 5th byte */ PAD(pad7) volatile unsigned char sbic_cdb6; /* rw: CDB, 6th byte */ PAD(pad8) volatile unsigned char sbic_cdb7; /* rw: CDB, 7th byte */ PAD(pad9) volatile unsigned char sbic_cdb8; /* rw: CDB, 8th byte */ PAD(pad10) volatile unsigned char sbic_cdb9; /* rw: CDB, 9th byte */ PAD(pad11) volatile unsigned char sbic_cdb10; /* rw: CDB, 10th byte */ PAD(pad12) volatile unsigned char sbic_cdb11; /* rw: CDB, 11th byte */ PAD(pad13) volatile unsigned char sbic_cdb12; /* rw: CDB, 12th byte */ PAD(pad14) volatile unsigned char sbic_tlun; /* rw: Target LUN */ PAD(pad15) volatile unsigned char sbic_cmd_phase; /* rw: Command phase */ PAD(pad16) volatile unsigned char sbic_syn; /* rw: Synch xfer params */ PAD(pad17) volatile unsigned char sbic_count_hi; /* rw: Xfer count, hi */ PAD(pad18) volatile unsigned char sbic_count_med; /* rw: Xfer count, med */ PAD(pad19) volatile unsigned char sbic_count_lo; /* rw: Xfer count, lo */ PAD(pad20) volatile unsigned char sbic_selid; /* rw: Target ID (select) */ PAD(pad21) volatile unsigned char sbic_rselid; /* rw: Target ID (reselect) */ PAD(pad22) volatile unsigned char sbic_csr; /* r : Status register */ PAD(pad23) volatile unsigned char sbic_cmd; /* rw: Command register */ PAD(pad24) volatile unsigned char sbic_data; /* rw: FIFO top */ PAD(pad25) char u0; /* unused, padding */ PAD(pad26) char u1; /* unused, padding */ PAD(pad27) char u2; /* unused, padding */ PAD(pad28) char u3; /* unused, padding */ PAD(pad29) char u4; /* unused, padding */ PAD(pad30) volatile unsigned char sbic_asr; /* r : Aux Status Register */ PAD(pad31) } sbic_padded_mux_regmap_t; typedef struct { volatile unsigned char sbic_asr; /* r : Aux Status Register */ /*#define sbic_address sbic_asr /* w : desired register no */ PAD(pad0); volatile unsigned char sbic_value; /* rw: register value */ PAD(pad1); } sbic_padded_ind_regmap_t; #else /* !PAD */ typedef sbic_mux_regmap_t sbic_padded_mux_regmap_t; typedef sbic_ind_regmap_t sbic_padded_ind_regmap_t; #endif /* !PAD */ /* * Could have used some non-ANSIsm in the following :-)) */ #ifdef SBIC_MUX_ADDRESSING typedef sbic_padded_mux_regmap_t sbic_padded_regmap_t; #define SET_SBIC_myid(regs,val) (regs)->sbic_myid = (val) #define GET_SBIC_myid(regs,val) (val) = (regs)->sbic_myid #define SET_SBIC_cdbsize(regs,val) (regs)->sbic_cdbsize = (val) #define GET_SBIC_cdbsize(regs,val) (val) = (regs)->sbic_cdbsize #define SET_SBIC_control(regs,val) (regs)->sbic_control = (val) #define GET_SBIC_control(regs,val) (val) = (regs)->sbic_control #define SET_SBIC_timeo(regs,val) (regs)->sbic_timeo = (val) #define GET_SBIC_timeo(regs,val) (val) = (regs)->sbic_timeo #define SET_SBIC_cdb1(regs,val) (regs)->sbic_cdb1 = (val) #define GET_SBIC_cdb1(regs,val) (val) = (regs)->sbic_cdb1 #define SET_SBIC_cdb2(regs,val) (regs)->sbic_cdb2 = (val) #define GET_SBIC_cdb2(regs,val) (val) = (regs)->sbic_cdb2 #define SET_SBIC_cdb3(regs,val) (regs)->sbic_cdb3 = (val) #define GET_SBIC_cdb3(regs,val) (val) = (regs)->sbic_cdb3 #define SET_SBIC_cdb4(regs,val) (regs)->sbic_cdb4 = (val) #define GET_SBIC_cdb4(regs,val) (val) = (regs)->sbic_cdb4 #define SET_SBIC_cdb5(regs,val) (regs)->sbic_cdb5 = (val) #define GET_SBIC_cdb5(regs,val) (val) = (regs)->sbic_cdb5 #define SET_SBIC_cdb6(regs,val) (regs)->sbic_cdb6 = (val) #define GET_SBIC_cdb6(regs,val) (val) = (regs)->sbic_cdb6 #define SET_SBIC_cdb7(regs,val) (regs)->sbic_cdb7 = (val) #define GET_SBIC_cdb7(regs,val) (val) = (regs)->sbic_cdb7 #define SET_SBIC_cdb8(regs,val) (regs)->sbic_cdb8 = (val) #define GET_SBIC_cdb8(regs,val) (val) = (regs)->sbic_cdb8 #define SET_SBIC_cdb9(regs,val) (regs)->sbic_cdb9 = (val) #define GET_SBIC_cdb9(regs,val) (val) = (regs)->sbic_cdb9 #define SET_SBIC_cdb10(regs,val) (regs)->sbic_cdb10 = (val) #define GET_SBIC_cdb10(regs,val) (val) = (regs)->sbic_cdb10 #define SET_SBIC_cdb11(regs,val) (regs)->sbic_cdb11 = (val) #define GET_SBIC_cdb11(regs,val) (val) = (regs)->sbic_cdb11 #define SET_SBIC_cdb12(regs,val) (regs)->sbic_cdb12 = (val) #define GET_SBIC_cdb12(regs,val) (val) = (regs)->sbic_cdb12 #define SET_SBIC_tlun(regs,val) (regs)->sbic_tlun = (val) #define GET_SBIC_tlun(regs,val) (val) = (regs)->sbic_tlun #define SET_SBIC_cmd_phase(regs,val) (regs)->sbic_cmd_phase = (val) #define GET_SBIC_cmd_phase(regs,val) (val) = (regs)->sbic_cmd_phase #define SET_SBIC_syn(regs,val) (regs)->sbic_syn = (val) #define GET_SBIC_syn(regs,val) (val) = (regs)->sbic_syn #define SET_SBIC_count_hi(regs,val) (regs)->sbic_count_hi = (val) #define GET_SBIC_count_hi(regs,val) (val) = (regs)->sbic_count_hi #define SET_SBIC_count_med(regs,val) (regs)->sbic_count_med = (val) #define GET_SBIC_count_med(regs,val) (val) = (regs)->sbic_count_med #define SET_SBIC_count_lo(regs,val) (regs)->sbic_count_lo = (val) #define GET_SBIC_count_lo(regs,val) (val) = (regs)->sbic_count_lo #define SET_SBIC_selid(regs,val) (regs)->sbic_selid = (val) #define GET_SBIC_selid(regs,val) (val) = (regs)->sbic_selid #define SET_SBIC_rselid(regs,val) (regs)->sbic_rselid = (val) #define GET_SBIC_rselid(regs,val) (val) = (regs)->sbic_rselid #define SET_SBIC_csr(regs,val) (regs)->sbic_csr = (val) #define GET_SBIC_csr(regs,val) (val) = (regs)->sbic_csr #define SET_SBIC_cmd(regs,val) (regs)->sbic_cmd = (val) #define GET_SBIC_cmd(regs,val) (val) = (regs)->sbic_cmd #define SET_SBIC_data(regs,val) (regs)->sbic_data = (val) #define GET_SBIC_data(regs,val) (val) = (regs)->sbic_data #define SBIC_TC_SET(regs,val) { \ (regs)->sbic_count_hi = ((val)>>16)); \ (regs)->sbic_count_med = (val)>>8; \ (regs)->sbic_count_lo = (val); \ } #define SBIC_TC_GET(regs,val) { \ (val) = ((regs)->sbic_count_hi << 16) | \ ((regs)->sbic_count_med << 8) | \ ((regs)->sbic_count_lo); \ } #define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) { \ register char *ptr = (char*)(cmd); \ (regs)->cis_cdb1 = *ptr++; \ (regs)->cis_cdb2 = *ptr++; \ (regs)->cis_cdb3 = *ptr++; \ (regs)->cis_cdb4 = *ptr++; \ (regs)->cis_cdb5 = *ptr++; \ (regs)->cis_cdb6 = *ptr++; \ if (cmdsize > 6) { \ (regs)->cis_cdb7 = *ptr++; \ (regs)->cis_cdb8 = *ptr++; \ (regs)->cis_cdb9 = *ptr++; \ (regs)->cis_cdb10 = *ptr++; \ } \ if (cmdsize > 10) { \ (regs)->cis_cdb11 = *ptr++; \ (regs)->cis_cdb12 = *ptr; \ } \ } #else /*SBIC_MUX_ADDRESSING*/ typedef sbic_padded_ind_regmap_t sbic_padded_regmap_t; #define SET_SBIC_myid(regs,val) sbic_write_reg(regs,SBIC_myid,val) #define GET_SBIC_myid(regs,val) sbic_read_reg(regs,SBIC_myid,val) #define SET_SBIC_cdbsize(regs,val) sbic_write_reg(regs,SBIC_cdbsize,val) #define GET_SBIC_cdbsize(regs,val) sbic_read_reg(regs,SBIC_cdbsize,val) #define SET_SBIC_control(regs,val) sbic_write_reg(regs,SBIC_control,val) #define GET_SBIC_control(regs,val) sbic_read_reg(regs,SBIC_control,val) #define SET_SBIC_timeo(regs,val) sbic_write_reg(regs,SBIC_timeo,val) #define GET_SBIC_timeo(regs,val) sbic_read_reg(regs,SBIC_timeo,val) #define SET_SBIC_cdb1(regs,val) sbic_write_reg(regs,SBIC_cdb1,val) #define GET_SBIC_cdb1(regs,val) sbic_read_reg(regs,SBIC_cdb1,val) #define SET_SBIC_cdb2(regs,val) sbic_write_reg(regs,SBIC_cdb2,val) #define GET_SBIC_cdb2(regs,val) sbic_read_reg(regs,SBIC_cdb2,val) #define SET_SBIC_cdb3(regs,val) sbic_write_reg(regs,SBIC_cdb3,val) #define GET_SBIC_cdb3(regs,val) sbic_read_reg(regs,SBIC_cdb3,val) #define SET_SBIC_cdb4(regs,val) sbic_write_reg(regs,SBIC_cdb4,val) #define GET_SBIC_cdb4(regs,val) sbic_read_reg(regs,SBIC_cdb4,val) #define SET_SBIC_cdb5(regs,val) sbic_write_reg(regs,SBIC_cdb5,val) #define GET_SBIC_cdb5(regs,val) sbic_read_reg(regs,SBIC_cdb5,val) #define SET_SBIC_cdb6(regs,val) sbic_write_reg(regs,SBIC_cdb6,val) #define GET_SBIC_cdb6(regs,val) sbic_read_reg(regs,SBIC_cdb6,val) #define SET_SBIC_cdb7(regs,val) sbic_write_reg(regs,SBIC_cdb7,val) #define GET_SBIC_cdb7(regs,val) sbic_read_reg(regs,SBIC_cdb7,val) #define SET_SBIC_cdb8(regs,val) sbic_write_reg(regs,SBIC_cdb8,val) #define GET_SBIC_cdb8(regs,val) sbic_read_reg(regs,SBIC_cdb8,val) #define SET_SBIC_cdb9(regs,val) sbic_write_reg(regs,SBIC_cdb9,val) #define GET_SBIC_cdb9(regs,val) sbic_read_reg(regs,SBIC_cdb9,val) #define SET_SBIC_cdb10(regs,val) sbic_write_reg(regs,SBIC_cdb10,val) #define GET_SBIC_cdb10(regs,val) sbic_read_reg(regs,SBIC_cdb10,val) #define SET_SBIC_cdb11(regs,val) sbic_write_reg(regs,SBIC_cdb11,val) #define GET_SBIC_cdb11(regs,val) sbic_read_reg(regs,SBIC_cdb11,val) #define SET_SBIC_cdb12(regs,val) sbic_write_reg(regs,SBIC_cdb12,val) #define GET_SBIC_cdb12(regs,val) sbic_read_reg(regs,SBIC_cdb12,val) #define SET_SBIC_tlun(regs,val) sbic_write_reg(regs,SBIC_tlun,val) #define GET_SBIC_tlun(regs,val) sbic_read_reg(regs,SBIC_tlun,val) #define SET_SBIC_cmd_phase(regs,val) sbic_write_reg(regs,SBIC_cmd_phase,val) #define GET_SBIC_cmd_phase(regs,val) sbic_read_reg(regs,SBIC_cmd_phase,val) #define SET_SBIC_syn(regs,val) sbic_write_reg(regs,SBIC_syn,val) #define GET_SBIC_syn(regs,val) sbic_read_reg(regs,SBIC_syn,val) #define SET_SBIC_count_hi(regs,val) sbic_write_reg(regs,SBIC_count_hi,val) #define GET_SBIC_count_hi(regs,val) sbic_read_reg(regs,SBIC_count_hi,val) #define SET_SBIC_count_med(regs,val) sbic_write_reg(regs,SBIC_count_med,val) #define GET_SBIC_count_med(regs,val) sbic_read_reg(regs,SBIC_count_med,val) #define SET_SBIC_count_lo(regs,val) sbic_write_reg(regs,SBIC_count_lo,val) #define GET_SBIC_count_lo(regs,val) sbic_read_reg(regs,SBIC_count_lo,val) #define SET_SBIC_selid(regs,val) sbic_write_reg(regs,SBIC_selid,val) #define GET_SBIC_selid(regs,val) sbic_read_reg(regs,SBIC_selid,val) #define SET_SBIC_rselid(regs,val) sbic_write_reg(regs,SBIC_rselid,val) #define GET_SBIC_rselid(regs,val) sbic_read_reg(regs,SBIC_rselid,val) #define SET_SBIC_csr(regs,val) sbic_write_reg(regs,SBIC_csr,val) #define GET_SBIC_csr(regs,val) sbic_read_reg(regs,SBIC_csr,val) #define SET_SBIC_cmd(regs,val) sbic_write_reg(regs,SBIC_cmd,val) #define GET_SBIC_cmd(regs,val) sbic_read_reg(regs,SBIC_cmd,val) #define SET_SBIC_data(regs,val) sbic_write_reg(regs,SBIC_data,val) #define GET_SBIC_data(regs,val) sbic_read_reg(regs,SBIC_data,val) #define SBIC_TC_SET(regs,val) { \ sbic_write_reg(regs,SBIC_count_hi,((val)>>16)); \ (regs)->sbic_value = (val)>>8; wbflush(); \ (regs)->sbic_value = (val); \ } #define SBIC_TC_GET(regs,val) { \ sbic_read_reg(regs,SBIC_count_hi,(val)); \ (val) = ((val)<<8) | (regs)->sbic_value; \ (val) = ((val)<<8) | (regs)->sbic_value; \ } #define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) { register int n=cmdsize-1; \ register char *ptr = (char*)(cmd); \ sbic_write_reg(regs,SBIC_cdb1,*ptr++); \ while (n-- > 0) (regs)->sbic_value = *ptr++; \ } #endif /*SBIC_MUX_ADDRESSING*/ #define GET_SBIC_asr(regs,val) (val) = (regs)->sbic_asr /* * If all goes well (cross fingers) the typical read/write operation * should complete in just one interrupt. Therefore our scripts * have only two parts: a pre-condition and an action. The first * triggers error handling if not satisfied and in our case it is a match * of .... * The action part is just a function pointer, invoked in a standard way. * The script proceeds only if the action routine returns TRUE. * See sbic_intr() for how and where this is all done. */ typedef struct script { struct { /* expected state at interrupt: */ unsigned char csr; /* interrupt cause */ unsigned char pha; /* command phase */ } condition; /* unsigned char unused[2]; /* unused padding */ boolean_t (*action)(); /* extra operations */ } *script_t; /* Matching on the condition value */ #define ANY 0xff #define SCRIPT_MATCH(csr,pha,cond) \ (((cond).csr == (csr)) && \ (((cond).pha == (pha)) || ((cond).pha==ANY))) /* forward decls of script actions */ boolean_t sbic_end(), /* all come to an end */ sbic_get_status(), /* get status from target */ sbic_dma_in(), /* get data from target via dma */ sbic_dma_in_r(), /* get data from target via dma (restartable)*/ sbic_dma_out(), /* send data to target via dma */ sbic_dma_out_r(), /* send data to target via dma (restartable) */ sbic_dosynch(), /* negotiate synch xfer */ sbic_msg_in(), /* receive the disconenct message */ sbic_disconnected(), /* target has disconnected */ sbic_reconnect(); /* target reconnected */ /* forward decls of error handlers */ boolean_t sbic_err_generic(), /* generic handler */ sbic_err_disconn(), /* target disconnects amidst */ gimmeabreak(); /* drop into the debugger */ int sbic_reset_scsibus(); boolean_t sbic_probe_target(); static sbic_wait(); /* * State descriptor for this layer. There is one such structure * per (enabled) SCSI-33c93 interface */ struct sbic_softc { watchdog_t wd; sbic_padded_regmap_t *regs; /* 33c93 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 SBIC_STATE_BUSY 0x01 /* selecting or currently connected */ #define SBIC_STATE_TARGET 0x04 /* currently selected as target */ #define SBIC_STATE_COLLISION 0x08 /* lost selection attempt */ #define SBIC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ #define SBIC_STATE_AM_MODE 0x20 /* 33c93A with advanced mode (AM) */ unsigned char ntargets; /* how many alive on this scsibus */ unsigned char done; unsigned char unused; 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 */ } sbic_softc_data[NSBIC]; typedef struct sbic_softc *sbic_softc_t; sbic_softc_t sbic_softc[NSBIC]; /* * Synch xfer parameters, and timing conversions */ int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */ int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */ int sbic_to_scsi_period(regs,a) { unsigned int fs; /* cycle = DIV / (2*CLK) */ /* DIV = FS+2 */ /* best we can do is 200ns at 20Mhz, 2 cycles */ GET_SBIC_myid(regs,fs); fs = (fs >>6) + 2; /* DIV */ fs = (fs * 1000) / (SBIC_CLOCK_FREQUENCY<<1); /* Cycle, in ns */ if (a < 2) a = 8; /* map to Cycles */ return ((fs*a)>>2); /* in 4 ns units */ } int scsi_period_to_sbic(regs,p) { register unsigned int fs; /* Just the inverse of the above */ GET_SBIC_myid(regs,fs); fs = (fs >>6) + 2; /* DIV */ fs = (fs * 1000) / (SBIC_CLOCK_FREQUENCY<<1); /* Cycle, in ns */ ret = p << 2; /* in ns units */ ret = ret / fs; /* in Cycles */ if (ret < sbic_min_period) return sbic_min_period; /* verify rounding */ if (sbic_to_scsi_period(regs,ret) < p) ret++; return (ret >= 8) ? 0 : ret; } #define u_min(a,b) (((a) < (b)) ? (a) : (b)) /* * Definition of the controller for the auto-configuration program. */ int sbic_probe(), scsi_slave(), scsi_attach(), sbic_go(), sbic_intr(); caddr_t sbic_std[NSBIC] = { 0 }; struct bus_device *sbic_dinfo[NSBIC*8]; struct bus_ctlr *sbic_minfo[NSBIC]; struct bus_driver sbic_driver = { sbic_probe, scsi_slave, scsi_attach, sbic_go, sbic_std, "rz", sbic_dinfo, "sbic", sbic_minfo, BUS_INTR_B4_PROBE}; sbic_set_dmaops(unit, dmaops) unsigned int unit; scsi_dma_ops_t *dmaops; { if (unit < NSBIC) sbic_std[unit] = (caddr_t)dmaops; } /* * Scripts */ struct script sbic_script_any_cmd[] = { /* started with SEL & XFER */ {{SBIC_CSR_S_XFERRED, 0x60}, sbic_get_status}, }, sbic_script_try_synch[] = { /* started with SEL */ {{SBIC_CSR_INITIATOR, ANY}, sbic_dosynch}, {{SBIC_CSR_S_XFERRED, 0x60}, sbic_get_status}, }; #define DEBUG #ifdef DEBUG #define PRINT(x) if (scsi_debug) printf x sbic_state(regs, overrule) sbic_padded_regmap_t *regs; { register unsigned char asr,tmp; if (regs == 0) { if (sbic_softc[0]) regs = sbic_softc[0]->regs; else regs = (sbic_padded_regmap_t*)0xXXXXXXXX; } GET_SBIC_asr(regs,asr); if ((asr & SBIC_ASR_BSY) && !overrule) db_printf("-BUSY- "); else { unsigned char tlun,pha,selid,rselid; unsigned int cnt; GET_SBIC_tlun(regs,tlun); GET_SBIC_cmd_phase(regs,pha); GET_SBIC_selid(regs,selid); GET_SBIC_rselid(regs,rselid); SBIC_TC_GET(regs,cnt); db_printf("tc %x tlun %x sel %x rsel %x pha %x ", cnt, tlun, selid, rselid, pha); } if (asr & SBIC_ASR_INT) db_printf("-INT- "); else { GET_SBIC_csr(regs,tmp); db_printf("csr %x ", tmp); } if (asr & SBIC_ASR_CIP) db_printf("-CIP-\n"); else { GET_SBIC_cmd(regs,tmp); db_printf("cmd %x\n", tmp); } return 0; } sbic_target_state(tgt) target_info_t *tgt; { if (tgt == 0) tgt = sbic_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.csr, spt->condition.pha); db_printsym(spt->action,1); db_printf(", "); db_printsym(tgt->transient_state.handler, 1); db_printf("\n"); } return 0; } sbic_all_targets(unit) { int i; target_info_t *tgt; for (i = 0; i < 8; i++) { tgt = sbic_softc[unit]->sc->target[i]; if (tgt) sbic_target_state(tgt); } } sbic_script_state(unit) { script_t spt = sbic_softc[unit]->script; if (spt == 0) return 0; db_printsym(spt,1); db_printf(": %x %x ", spt->condition.csr, spt->condition.pha); db_printsym(spt->action,1); db_printf(", "); db_printsym(sbic_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} #define TRACE #ifdef TRACE #define LOGSIZE 256 int sbic_logpt; char sbic_log[LOGSIZE]; #define MAXLOG_VALUE 0x1e struct { char *name; unsigned int count; } logtbl[MAXLOG_VALUE]; static LOG(e,f) char *f; { sbic_log[sbic_logpt++] = (e); if (sbic_logpt == LOGSIZE) sbic_logpt = 0; if ((e) < MAXLOG_VALUE) { logtbl[(e)].name = (f); logtbl[(e)].count++; } } sbic_print_log(skip) int skip; { register int i, j; register unsigned char c; for (i = 0, j = sbic_logpt; i < LOGSIZE; i++) { c = sbic_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); } } sbic_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 #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. */ sbic_probe(reg, ui) unsigned reg; struct bus_ctlr *ui; { int unit = ui->unit; sbic_softc_t sbic = &sbic_softc_data[unit]; int target_id; scsi_softc_t *sc; register sbic_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 MAPPABLE /* Mappable version side */ SBIC_probe(reg, ui); #endif /*MAPPABLE*/ /* * Initialize hw descriptor, cache some pointers */ sbic_softc[unit] = sbic; sbic->regs = (sbic_padded_regmap_t *) (reg); if ((sbic->dma_ops = (scsi_dma_ops_t *)sbic_std[unit]) == 0) /* use same as unit 0 if undefined */ sbic->dma_ops = (scsi_dma_ops_t *)sbic_std[0]; sbic->dma_state = (*sbic->dma_ops->init)(unit, reg); queue_init(&sbic->waiting_targets); sc = scsi_master_alloc(unit, sbic); sbic->sc = sc; sc->go = sbic_go; sc->watchdog = scsi_watchdog; sc->probe = sbic_probe_target; sbic->wd.reset = sbic_reset_scsibus; #ifdef MACH_KERNEL sc->max_dma_data = -1; #else sc->max_dma_data = scsi_per_target_virtual; #endif regs = sbic->regs; /* * Reset chip, fully. Note that interrupts are already enabled. */ s = splbio(); if (sbic_reset(regs, TRUE)) sbic->state |= SBIC_STATE_AM_MODE; /* * Our SCSI id on the bus. * The user can probably set this via the prom. * If not, it is easy to fix: make a default that * can be changed as boot arg. Otherwise we keep * what the prom used. */ #ifdef unneeded SET_SBIC_myid(regs, (scsi_initiator_id[unit] & 0x7)); sbic_reset(regs, TRUE); #endif GET_SBIC_myid(regs,sc->initiator_id); sc->initiator_id &= 0x7; printf("%s%d: SCSI id %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; target_id < 8; target_id++) { register unsigned char asr, csr, pha; register scsi_status_byte_t status; /* except of course ourselves */ if (target_id == sc->initiator_id) continue; SBIC_TC_SET(regs,0); SET_SBIC_selid(regs,target_id); SET_SBIC_timo(regs,SBIC_TIMEOUT(250,SBIC_CLOCK_FREQUENCY)); /* * See if the unit is ready. * XXX SHOULD inquiry LUN 0 instead !!! */ { scsi_command_test_unit_ready_y c; bzero(&c, sizeof(c)); c.scsi_cmd_code = SCSI_CMD_TEST_UNIT_READY; SBIC_LOAD_COMMAND(regs,&c,sizeof(c)); } /* select and send it */ SET_SBIC_cmd(regs,SBIC_CMD_SEL_XFER); /* wait for the chip to complete, or timeout */ asr = sbic_wait(regs, SBIC_ASR_INT); GET_SBIC_csr(regs,csr); /* * Check if the select timed out */ GET_SBIC_cmd_phase(regs,pha); if ((SBIC_CPH(pha) == 0) && (csr & SBIC_CSR_CMD_ERR)) { /* noone out there */ #if notsure SET_SBIC_cmd(regs,SBIC_CMD_DISC); asr = sbic_wait(regs, SBIC_ASR_INT); GET_SBIC_csr(regs,csr); #endif continue; } printf(",%s%d", did_banner++ ? " " : " target(s) at ", target_id); if (SBIC_CPH(pha) < 0x60) /* XXX recover by hand XXX */ panic(" target acts weirdo"); GET_SBIC_tlun(regs,status.bits); if (status.st.scsi_status_code != SCSI_ST_GOOD) scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); /* * Found a target */ sbic->ntargets++; { register target_info_t *tgt; tgt = scsi_slave_alloc(sc->masterno, target_id, sbic); (*sbic->dma_ops->new_target)(sbic->dma_state, tgt); } } printf(".\n"); splx(s); return 1; } boolean_t sbic_probe_target(tgt, ior) target_info_t *tgt; io_req_t ior; { sbic_softc_t sbic = sbic_softc[tgt->masterno]; boolean_t newlywed; newlywed = (tgt->cmd_ptr == 0); if (newlywed) { (*sbic->dma_ops->new_target)(sbic->dma_state, tgt); } if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) return FALSE; tgt->flags = TGT_ALIVE; return TRUE; } static sbic_wait(regs, until) sbic_padded_regmap_t *regs; char until; { register unsigned char val; int timeo = 1000000; GET_SBIC_asr(regs,val); while ((val & until) == 0) { if (!timeo--) { printf("sbic_wait TIMEO with x%x\n", regs->sbic_csr); break; } delay(1); GET_SBIC_asr(regs,val); } return val; } boolean_t sbic_reset(regs, quick) sbic_padded_regmap_t *regs; { char my_id, csr; /* preserve our ID for now */ GET_SBIC_myid(regs,my_id); my_id &= SBIC_ID_MASK; if (SBIC_CLOCK_FREQUENCY < 11) my_id |= SBIC_ID_FS_8_10; else if (SBIC_CLOCK_FREQUENCY < 16) my_id |= SBIC_ID_FS_12_15; else if (SBIC_CLOCK_FREQUENCY < 21) my_id |= SBIC_ID_FS_16_20; my_id |= SBIC_ID_EAF|SBIC_ID_EHP; SET_SBIC_myid(regs,myid); wbflush(); /* * Reset chip and wait till done */ SET_SBIC_cmd(regs,SBIC_CMD_RESET); delay(25); (void) sbic_wait(regs, SBIC_ASR_INT); GET_SBIC_csr(regs,csr); /* clears interrupt also */ /* * Set up various chip parameters */ SET_SBIC_control(regs, SBIC_CTL_HHP|SBIC_CTL_EDI|SBIC_CTL_HSP| SBIC_MACHINE_DMA_MODE); /* will do IDI on the fly */ SET_SBIC_rselid(regs, SBIC_RID_ER|SBIC_RID_ES|SBIC_RID_DSP); SET_SBIC_syn(regs,SBIC_SYN(0,sbic_min_period)); /* asynch for now */ /* anything else was zeroed by reset */ if (quick) return (csr & SBIC_CSR_RESET_AM); /* * reset the scsi bus, the interrupt routine does the rest * or you can call sbic_bus_reset(). */ /* * Now HOW do I do this ? I just want to drive the SCSI "RST" * signal true for about 25 usecs; But the chip has no notion * of such a signal at all. The spec suggest that the chip's * reset pin be connected to the RST signal, which makes this * operation a machdep one. */ SBIC_MACHINE_RESET_SCSIBUS(regs, 30); return (csr & SBIC_CSR_RESET_AM); } /* * Operational functions */ /* * Start a SCSI command on a target */ sbic_go(tgt, cmd_count, in_count, cmd_only) target_info_t *tgt; boolean_t cmd_only; { sbic_softc_t sbic; register spl_t s; boolean_t disconn; script_t scp; boolean_t (*handler)(); LOG(1,"go"); sbic = (sbic_softc_t)tgt->hw_state; tgt->transient_state.cmd_count = cmd_count; /* keep it here */ (*sbic->dma_ops->map)(sbic->dma_state, tgt); disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id); disconn = disconn && (sbic->ntargets > 1); disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); /* * Setup target state */ tgt->done = SCSI_RET_IN_PROGRESS; handler = (disconn) ? sbic_err_disconn : sbic_err_generic; scp = sbic_script_any_cmd; switch (tgt->cur_cmd) { case SCSI_CMD_READ: case SCSI_CMD_LONG_READ: LOG(2,"readop"); break; case SCSI_CMD_WRITE: case SCSI_CMD_LONG_WRITE: LOG(0x1a,"writeop"); 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 = sbic_script_try_synch; tgt->flags |= TGT_TRY_SYNCH; break; } case SCSI_CMD_MODE_SELECT: case SCSI_CMD_REASSIGN_BLOCKS: case SCSI_CMD_FORMAT_UNIT: tgt->transient_state.cmd_count = sizeof(scsi_command_group_0); tgt->transient_state.out_count = cmd_count - sizeof(scsi_command_group_0); /* fall through */ 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: 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 = sbic_script_try_synch; tgt->flags |= TGT_TRY_SYNCH; cmd_only = FALSE; } /* fall through */ default: LOG(0x1c,"cmdop"); LOG(0x80+tgt->cur_cmd,0); break; } 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 sbic 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 (sbic->wd.nactive++ == 0) sbic->wd.watchdog_state = SCSI_WD_ACTIVE; if (sbic->state & SBIC_STATE_BUSY) { /* * Queue up this target, note that this takes care * of proper FIFO scheduling of the scsi-bus. */ LOG(3,"enqueue"); enqueue_tail(&sbic->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. */ sbic->state |= SBIC_STATE_BUSY; sbic->next_target = tgt; sbic_attempt_selection(sbic); /* * Note that we might still lose arbitration.. */ } splx(s); } sbic_attempt_selection(sbic) sbic_softc_t sbic; { target_info_t *tgt; sbic_padded_regmap_t *regs; register unsigned char val; register int out_count; regs = sbic->regs; tgt = sbic->next_target; LOG(4,"select"); LOG(0x80+tgt->target_id,0); /* * We own the bus now.. unless we lose arbitration */ sbic->active_target = tgt; /* Try to avoid reselect collisions */ GET_SBIC_asr(regs,val); if (val & SBIC_ASR_INT) return; /* * Init bus state variables */ sbic->script = tgt->transient_state.script; sbic->error_handler = tgt->transient_state.handler; sbic->done = SCSI_RET_IN_PROGRESS; sbic->out_count = 0; sbic->in_count = 0; /* Define how the identify msg should be built */ GET_SBIC_rselid(regs, val); val &= ~(SBIC_RID_MASK|SBIC_RID_ER); /* the enable reselection bit is used to build the identify msg */ if (tgt->transient_state.identify != 0xff) val |= (tgt->transient_state.identify & SCSI_IFY_ENABLE_DISCONNECT) << 1; SET_SBIC_rselid(regs, val); SET_SBIC_tlun(regs, tgt->lun); /* * Start the chip going */ out_count = (*sbic->dma_ops->start_cmd)(sbic->dma_state, tgt); SBIC_TC_PUT(regs, out_count); val = tgt->target_id; if (tgt->transient_state.in_count) val |= SBIC_SID_FROM_SCSI; SET_SBIC_selid(regs, val); SET_SBIC_timo(regs,SBIC_TIMEOUT(250,SBIC_CLOCK_FREQUENCY)); SET_SBIC_syn(regs,SBIC_SYN(tgt->sync_offset,tgt->sync_period)); /* ugly little help for compiler */ #define command out_count if (tgt->flags & TGT_DID_SYNCH) { command = (tgt->transient_state.identify == 0xff) ? SBIC_CMD_SEL_XFER : SBIC_CMD_SEL_ATN_XFER; /*preferred*/ } else if (tgt->flags & TGT_TRY_SYNCH) command = SBIC_CMD_SEL_ATN; else command = SBIC_CMD_SEL_XFER; /* load 10 bytes anyways, the chip knows how much to use */ SBIC_LOAD_COMMAND(regs, tgt->cmd_ptr, 10); /* Try to avoid reselect collisions */ GET_SBIC_asr(regs,val); if (val & SBIC_ASR_INT) return; SET_SBIC_cmd_phase(regs, 0); /* not a resume */ SET_SBIC_cmd(regs, command); #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. */ sbic_intr(unit, spllevel) spl_t spllevel; { register sbic_softc_t sbic; register script_t scp; register int asr, csr, pha; register sbic_padded_regmap_t *regs; #if MAPPABLE extern boolean_t rz_use_mapped_interface; if (rz_use_mapped_interface) return SBIC_intr(unit,spllevel); #endif /*MAPPABLE*/ sbic = sbic_softc[unit]; regs = sbic->regs; LOG(5,"\n\tintr"); /* drop spurious interrupts */ GET_SBIC_asr(regs, asr); if ((asr & SBIC_ASR_INT) == 0) return; /* collect ephemeral information */ GET_SBIC_cmd_phase(regs, pha); GET_SBIC_csr(regs, csr); TR(csr);TR(asr);TR(pha);TRCHECK /* XXX verify this is indeed the case for a SCSI RST asserted */ if ((csr & SBIC_CSR_CAUSE) == SBIC_CSR_RESET) return sbic_bus_reset(sbic); /* we got an interrupt allright */ if (sbic->active_target) sbic->wd.watchdog_state = SCSI_WD_ACTIVE; splx(spllevel); /* drop priority */ if ((sbic->state & SBIC_STATE_TARGET) || (csr == SBIC_CSR_RSLT_AM) || (csr == SBIC_CSR_RSLT_NOAM) || (csr == SBIC_CSR_SLT) || (csr == SBIC_CSR_SLT_ATN)) return sbic_target_intr(sbic); /* * In attempt_selection() we gave the select command even if * the chip might have been reconnected already. */ if ((csr == SBIC_CSR_RSLT_NI) || (csr == SBIC_CSR_RSLT_IFY)) return sbic_reconnect(sbic, csr, pha); /* * Check for parity errors */ if (asr & SBIC_ASR_PE) { char *msg; printf("{PE %x,%x}", asr, pha); msg = "SCSI bus parity error"; /* all we can do is to throw a reset on the bus */ printf( "sbic%d: %s%s", sbic - sbic_softc_data, msg, ", attempting recovery.\n"); sbic_reset(regs, FALSE); return; } if ((scp = sbic->script) == 0) /* sanity */ return; LOG(6,"match"); if (SCRIPT_MATCH(csr,pha,scp->condition)) { /* * Perform the appropriate operation, * then proceed */ if ((*scp->action)(sbic, csr, pha)) { sbic->script = scp + 1; } } else return (*sbic->error_handler)(sbic, csr, pha); } sbic_target_intr() { panic("SBIC: TARGET MODE !!!\n"); } /* * Routines that the interrupt code might switch to */ boolean_t sbic_end(sbic, csr, pha) register sbic_softc_t sbic; { register target_info_t *tgt; register io_req_t ior; LOG(8,"end"); tgt = sbic->active_target; if ((tgt->done = sbic->done) == SCSI_RET_IN_PROGRESS) tgt->done = SCSI_RET_SUCCESS; sbic->script = 0; if (sbic->wd.nactive-- == 1) sbic->wd.watchdog_state = SCSI_WD_INACTIVE; sbic_release_bus(sbic); if (ior = tgt->ior) { (*sbic->dma_ops->end_cmd)(sbic->dma_state, tgt, ior); LOG(0xA,"ops->restart"); (*tgt->dev_ops->restart)( tgt, TRUE); } return FALSE; } boolean_t sbic_release_bus(sbic) register sbic_softc_t sbic; { boolean_t ret = TRUE; LOG(9,"release"); if (sbic->state & SBIC_STATE_COLLISION) { LOG(0xB,"collided"); sbic->state &= ~SBIC_STATE_COLLISION; sbic_attempt_selection(sbic); } else if (queue_empty(&sbic->waiting_targets)) { sbic->state &= ~SBIC_STATE_BUSY; sbic->active_target = 0; sbic->script = 0; ret = FALSE; } else { LOG(0xC,"dequeue"); sbic->next_target = (target_info_t *) dequeue_head(&sbic->waiting_targets); sbic_attempt_selection(sbic); } return ret; } boolean_t sbic_get_status(sbic, csr, pha) register sbic_softc_t sbic; { register sbic_padded_regmap_t *regs = sbic->regs; register scsi2_status_byte_t status; int len; io_req_t ior; register target_info_t *tgt = sbic->active_target; LOG(0xD,"get_status"); TRWRAP sbic->state &= ~SBIC_STATE_DMA_IN; /* * Get the status byte */ GET_SBIC_tlun(regs, status.bits); if (status.st.scsi_status_code != SCSI_ST_GOOD) { scsi_error(sbic->active_target, SCSI_ERR_STATUS, status.bits, 0); sbic->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; } else sbic->done = SCSI_RET_SUCCESS; /* Tell DMA engine we are done */ (*sbic->dma_ops->end_xfer)(sbic->dma_state, tgt, tgt->transient_state.in_count); return sbic_end(sbic, csr, pha); } #if 0 boolean_t sbic_dma_in(sbic, csr, ir) register sbic_softc_t sbic; { register target_info_t *tgt; register sbic_padded_regmap_t *regs = sbic->regs; register int count; unsigned char ff = regs->sbic_flags; LOG(0xE,"dma_in"); tgt = sbic->active_target; sbic->state |= SBIC_STATE_DMA_IN; count = (*sbic->dma_ops->start_datain)(sbic->dma_state, tgt); SBIC_TC_PUT(regs, count); if ((sbic->in_count = count) == tgt->transient_state.in_count) return TRUE; regs->sbic_cmd = sbic->script->command; sbic->script = sbic_script_restart_data_in; return FALSE; } sbic_dma_in_r(sbic, csr, ir) register sbic_softc_t sbic; { register target_info_t *tgt; register sbic_padded_regmap_t *regs = sbic->regs; register int count; boolean_t advance_script = TRUE; LOG(0xE,"dma_in"); tgt = sbic->active_target; sbic->state |= SBIC_STATE_DMA_IN; if (sbic->in_count == 0) { /* * Got nothing yet, we just reconnected. */ register int avail; /* * Rather than using the messy RFB bit in cnfg2 * (which only works for synch xfer anyways) * we just bump up the dma offset. We might * endup with one more interrupt at the end, * so what. * This is done in sbic_err_disconn(), this * way dma (of msg bytes too) is always aligned */ count = (*sbic->dma_ops->restart_datain_1) (sbic->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. */ SBIC_TC_GET(regs,xferred); if (xferred != 0) return FALSE; xferred = sbic->in_count - xferred; assert(xferred > 0); tgt->transient_state.in_count -= xferred; assert(tgt->transient_state.in_count > 0); count = (*sbic->dma_ops->restart_datain_2) (sbic->dma_state, tgt, xferred); sbic->in_count = count; SBIC_TC_PUT(regs, count); regs->sbic_cmd = sbic->script->command; (*sbic->dma_ops->restart_datain_3) (sbic->dma_state, tgt); /* last chunk ? */ if (count == tgt->transient_state.in_count) sbic->script++; return FALSE; } sbic->in_count = count; SBIC_TC_PUT(regs, count); if (!advance_script) { regs->sbic_cmd = sbic->script->command; } return advance_script; } /* send data to target. Only called to start the xfer */ boolean_t sbic_dma_out(sbic, csr, ir) register sbic_softc_t sbic; { register sbic_padded_regmap_t *regs = sbic->regs; register int reload_count; register target_info_t *tgt; int command; LOG(0xF,"dma_out"); SBIC_TC_GET(regs, reload_count); sbic->extra_count = regs->sbic_flags & SBIC_FLAGS_FIFO_CNT; reload_count += sbic->extra_count; SBIC_TC_PUT(regs, reload_count); sbic->state &= ~SBIC_STATE_DMA_IN; tgt = sbic->active_target; command = sbic->script->command; if ((sbic->out_count = reload_count) >= tgt->transient_state.out_count) sbic->script++; else sbic->script = sbic_script_restart_data_out; if ((*sbic->dma_ops->start_dataout) (sbic->dma_state, tgt, ®s->sbic_cmd, command)) { regs->sbic_cmd = command; } return FALSE; } /* send data to target. Called in two different ways: (a) to restart a big transfer and (b) after reconnection */ boolean_t sbic_dma_out_r(sbic, csr, ir) register sbic_softc_t sbic; { register sbic_padded_regmap_t *regs = sbic->regs; register target_info_t *tgt; boolean_t advance_script = TRUE; int count; LOG(0xF,"dma_out"); tgt = sbic->active_target; sbic->state &= ~SBIC_STATE_DMA_IN; if (sbic->out_count == 0) { /* * Nothing committed: we just got reconnected */ count = (*sbic->dma_ops->restart_dataout_1) (sbic->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; SBIC_TC_GET(regs,count); /* see comment above */ if (count) { return FALSE; } count += (regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); count -= sbic->extra_count; xferred = sbic->out_count - count; assert(xferred > 0); tgt->transient_state.out_count -= xferred; assert(tgt->transient_state.out_count > 0); count = (*sbic->dma_ops->restart_dataout_2) (sbic->dma_state, tgt, xferred); /* last chunk ? */ if (tgt->transient_state.out_count == count) goto quickie; sbic->out_count = count; sbic->extra_count = (*sbic->dma_ops->restart_dataout_3) (sbic->dma_state, tgt, ®s->sbic_fifo); SBIC_TC_PUT(regs, count); regs->sbic_cmd = sbic->script->command; (*sbic->dma_ops->restart_dataout_4)(sbic->dma_state, tgt); return FALSE; } quickie: sbic->extra_count = (*sbic->dma_ops->restart_dataout_3) (sbic->dma_state, tgt, ®s->sbic_fifo); sbic->out_count = count; SBIC_TC_PUT(regs, count); if (!advance_script) { regs->sbic_cmd = sbic->script->command; } return advance_script; } #endif /*0*/ boolean_t sbic_dosynch(sbic, csr, pha) register sbic_softc_t sbic; register unsigned char csr, pha; { register sbic_padded_regmap_t *regs = sbic->regs; register unsigned char c; int i, per, offs; register target_info_t *tgt; /* * Try synch negotiation * Phase is MSG_OUT here. */ tgt = sbic->active_target; #if 0 regs->sbic_cmd = SBIC_CMD_FLUSH; delay(2); per = sbic_min_period; if (BGET(scsi_no_synchronous_xfer,sbic->sc->masterno,tgt->target_id)) offs = 0; else offs = sbic_max_offset; tgt->flags |= TGT_DID_SYNCH; /* only one chance */ tgt->flags &= ~TGT_TRY_SYNCH; regs->sbic_fifo = SCSI_EXTENDED_MESSAGE; regs->sbic_fifo = 3; regs->sbic_fifo = SCSI_SYNC_XFER_REQUEST; regs->sbic_fifo = sbic_to_scsi_period(regs,sbic_min_period); regs->sbic_fifo = offs; regs->sbic_cmd = SBIC_CMD_XFER_INFO; csr = sbic_wait(regs, SBIC_CSR_INT); ir = regs->sbic_intr; if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) gimmeabreak(); regs->sbic_cmd = SBIC_CMD_XFER_INFO; csr = sbic_wait(regs, SBIC_CSR_INT); ir = regs->sbic_intr; while ((regs->sbic_flags & SBIC_FLAGS_FIFO_CNT) > 0) c = regs->sbic_fifo; /* see what it says */ if (c == SCSI_MESSAGE_REJECT) { printf(" did not like SYNCH xfer "); /* Tk50s get in trouble with ATN, sigh. */ regs->sbic_cmd = SBIC_CMD_CLR_ATN; goto cmd; } /* * Receive the rest of the message */ regs->sbic_cmd = SBIC_CMD_MSG_ACPT; sbic_wait(regs, SBIC_CSR_INT); ir = regs->sbic_intr; if (c != SCSI_EXTENDED_MESSAGE) gimmeabreak(); regs->sbic_cmd = SBIC_CMD_XFER_INFO; sbic_wait(regs, SBIC_CSR_INT); c = regs->sbic_intr; if (regs->sbic_fifo != 3) panic("sbic_dosynch"); for (i = 0; i < 3; i++) { regs->sbic_cmd = SBIC_CMD_MSG_ACPT; sbic_wait(regs, SBIC_CSR_INT); c = regs->sbic_intr; regs->sbic_cmd = SBIC_CMD_XFER_INFO; sbic_wait(regs, SBIC_CSR_INT); c = regs->sbic_intr;/*ack*/ c = regs->sbic_fifo; if (i == 1) tgt->sync_period = scsi_period_to_sbic(regs,c); if (i == 2) tgt->sync_offset = c; } cmd: regs->sbic_cmd = SBIC_CMD_MSG_ACPT; csr = sbic_wait(regs, SBIC_CSR_INT); c = regs->sbic_intr; /* phase should normally be command here */ if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) { /* test unit ready or what ? */ regs->sbic_fifo = 0; regs->sbic_fifo = 0; regs->sbic_fifo = 0; regs->sbic_fifo = 0; regs->sbic_fifo = 0; regs->sbic_fifo = 0; SBIC_TC_PUT(regs,0xff); regs->sbic_cmd = SBIC_CMD_XFER_PAD; /*0x98*/ csr = sbic_wait(regs, SBIC_CSR_INT); ir = regs->sbic_intr;/*ack*/ } status: if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) gimmeabreak(); #endif return TRUE; } /* * The bus was reset */ sbic_bus_reset(sbic) register sbic_softc_t sbic; { register sbic_padded_regmap_t *regs = sbic->regs; LOG(0x1d,"bus_reset"); /* * Clear bus descriptor */ sbic->script = 0; sbic->error_handler = 0; sbic->active_target = 0; sbic->next_target = 0; sbic->state &= SBIC_STATE_AM_MODE; /* save this one bit only */ queue_init(&sbic->waiting_targets); sbic->wd.nactive = 0; (void) sbic_reset(regs, TRUE); printf("sbic: (%d) bus reset ", ++sbic->wd.reset_count); delay(scsi_delay_after_reset); /* some targets take long to reset */ if (sbic->sc == 0) /* sanity */ return; scsi_bus_was_reset(sbic->sc); } /* * Disconnect/reconnect mode ops */ /* save all relevant data, free the BUS */ boolean_t sbic_disconnected(sbic, csr, pha) register sbic_softc_t sbic; register unsigned char csr, pha; { register target_info_t *tgt; LOG(0x11,"disconnected"); tgt = sbic->active_target; tgt->flags |= TGT_DISCONNECTED; tgt->transient_state.handler = sbic->error_handler; /* anything else was saved in sbic_err_disconn() */ PRINT(("{D%d}", tgt->target_id)); sbic_release_bus(sbic); return FALSE; } /* See who reconnected, restore BUS */ boolean_t sbic_reconnect(sbic, csr, ir) register sbic_softc_t sbic; register unsigned char csr, ir; { register target_info_t *tgt; sbic_padded_regmap_t *regs; int id, pha; LOG(0x12,"reconnect"); /* * See if this reconnection collided with a selection attempt */ if (sbic->state & SBIC_STATE_BUSY) sbic->state |= SBIC_STATE_COLLISION; sbic->state |= SBIC_STATE_BUSY; /* find tgt */ regs = sbic->regs; GET_SBIC_rselid(regs,id); id &= 0x7; if ((sbic->state & SBIC_STATE_AM) == 0) { /* Must pick the identify */ pha = 0x44; } else pha = 0x45; tgt = sbic->sc->target[id]; if (id > 7 || tgt == 0) panic("sbic_reconnect"); /* synch things*/ SET_SBIC_syn(regs,SBIC_SYN(tgt->sync_offset,tgt->sync_period)); PRINT(("{R%d}", id)); if (sbic->state & SBIC_STATE_COLLISION) PRINT(("[B %d-%d]", sbic->active_target->target_id, id)); LOG(0x80+id,0); sbic->active_target = tgt; tgt->flags &= ~TGT_DISCONNECTED; sbic->script = tgt->transient_state.script; sbic->error_handler = tgt->transient_state.handler; sbic->in_count = 0; sbic->out_count = 0; set counter and setup dma, then /* Resume the command now */ SET_SBIC_cmd_phase(regs, pha); SET_SBIC_cmd(regs, SBIC_CMD_SEL_XFER); return FALSE; } TILL HERE /* * Error handlers */ /* * Fall-back error handler. */ sbic_err_generic(sbic, csr, ir) register sbic_softc_t sbic; { LOG(0x13,"err_generic"); /* handle non-existant or powered off devices here */ if ((ir == SBIC_INT_DISC) && (sbic_isa_select(sbic->cmd_was)) && (SBIC_SS(sbic->ss_was) == 0)) { /* Powered off ? */ if (sbic->active_target->flags & TGT_FULLY_PROBED) sbic->active_target->flags = 0; sbic->done = SCSI_RET_DEVICE_DOWN; sbic_end(sbic, csr, ir); return; } switch (SCSI_PHASE(csr)) { case SCSI_PHASE_STATUS: if (sbic->script[-1].condition == SCSI_PHASE_STATUS) { /* some are just slow to get out.. */ } else sbic_err_to_status(sbic, csr, ir); return; break; case SCSI_PHASE_DATAI: if (sbic->script->condition == SCSI_PHASE_STATUS) { /* printf("{P}");*/ return; } break; case SCSI_PHASE_DATAO: if (sbic->script->condition == SCSI_PHASE_STATUS) { /* * See comment above. Actually seen on hitachis. */ /* printf("{P}");*/ return; } } gimmeabreak(); } /* * Handle disconnections as exceptions */ sbic_err_disconn(sbic, csr, ir) register sbic_softc_t sbic; register unsigned char csr, ir; { register sbic_padded_regmap_t *regs; register target_info_t *tgt; int count; boolean_t callback = FALSE; LOG(0x16,"err_disconn"); if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) return sbic_err_generic(sbic, csr, ir); regs = sbic->regs; tgt = sbic->active_target; switch (sbic->script->condition) { case SCSI_PHASE_DATAO: LOG(0x1b,"+DATAO"); if (sbic->out_count) { register int xferred, offset; SBIC_TC_GET(regs,xferred); /* temporary misnomer */ xferred += regs->sbic_flags & SBIC_FLAGS_FIFO_CNT; xferred -= sbic->extra_count; xferred = sbic->out_count - xferred; /* ok now */ tgt->transient_state.out_count -= xferred; assert(tgt->transient_state.out_count > 0); callback = (*sbic->dma_ops->disconn_1) (sbic->dma_state, tgt, xferred); } else { callback = (*sbic->dma_ops->disconn_2) (sbic->dma_state, tgt); } sbic->extra_count = 0; tgt->transient_state.script = sbic_script_restart_data_out; break; case SCSI_PHASE_DATAI: LOG(0x17,"+DATAI"); if (sbic->in_count) { register int offset, xferred; SBIC_TC_GET(regs,count); xferred = sbic->in_count - count; assert(xferred > 0); if (regs->sbic_flags & 0xf) printf("{Xf %x,%x,%x}", xferred, sbic->in_count, regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); tgt->transient_state.in_count -= xferred; assert(tgt->transient_state.in_count > 0); callback = (*sbic->dma_ops->disconn_3) (sbic->dma_state, tgt, xferred); tgt->transient_state.script = sbic_script_restart_data_in; if (tgt->transient_state.in_count == 0) tgt->transient_state.script++; } tgt->transient_state.script = sbic->script; break; case SCSI_PHASE_STATUS: /* will have to restart dma */ SBIC_TC_GET(regs,count); if (sbic->state & SBIC_STATE_DMA_IN) { register int offset, xferred; LOG(0x1a,"+STATUS+R"); xferred = sbic->in_count - count; assert(xferred > 0); if (regs->sbic_flags & 0xf) printf("{Xf %x,%x,%x}", xferred, sbic->in_count, regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); tgt->transient_state.in_count -= xferred; /* assert(tgt->transient_state.in_count > 0);*/ callback = (*sbic->dma_ops->disconn_4) (sbic->dma_state, tgt, xferred); tgt->transient_state.script = sbic_script_restart_data_in; if (tgt->transient_state.in_count == 0) tgt->transient_state.script++; } else { /* add what's left in the fifo */ count += (regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); /* take back the extra we might have added */ count -= sbic->extra_count; /* ..and drop that idea */ sbic->extra_count = 0; LOG(0x19,"+STATUS+W"); if ((count == 0) && (tgt->transient_state.out_count == sbic->out_count)) { /* all done */ tgt->transient_state.script = sbic->script; tgt->transient_state.out_count = 0; } else { register int xferred, offset; /* how much we xferred */ xferred = sbic->out_count - count; tgt->transient_state.out_count -= xferred; assert(tgt->transient_state.out_count > 0); callback = (*sbic->dma_ops->disconn_5) (sbic->dma_state,tgt,xferred); tgt->transient_state.script = sbic_script_restart_data_out; } sbic->out_count = 0; } break; default: gimmeabreak(); return; } sbic_msg_in(sbic,csr,ir); sbic->script = sbic_script_disconnect; regs->sbic_cmd = SBIC_CMD_XFER_INFO|SBIC_CMD_DMA; if (callback) (*sbic->dma_ops->disconn_callback)(sbic->dma_state, tgt); } /* * Watchdog * * We know that some (name withdrawn) disks get * stuck in the middle of dma phases... */ sbic_reset_scsibus(sbic) register sbic_softc_t sbic; { register target_info_t *tgt = sbic->active_target; register sbic_padded_regmap_t *regs = sbic->regs; register int ir; if (scsi_debug && tgt) { int dmalen; SBIC_TC_GET(sbic->regs,dmalen); 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, sbic->in_count, sbic->out_count, dmalen); } ir = regs->sbic_intr; if ((ir & SBIC_INT_RESEL) && (SCSI_PHASE(regs->sbic_csr) == SCSI_PHASE_MSG_IN)) { /* getting it out of the woods is a bit tricky */ spl_t s = splbio(); (void) sbic_reconnect(sbic, regs->sbic_csr, ir); sbic_wait(regs, SBIC_CSR_INT); ir = regs->sbic_intr; regs->sbic_cmd = SBIC_CMD_MSG_ACPT; splx(s); } else { regs->sbic_cmd = SBIC_CMD_BUS_RESET; delay(35); } } #endif NSBIC > 0 #endif 0