diff options
Diffstat (limited to 'scsi/adapters/scsi_33C93_hdw.c')
-rw-r--r-- | scsi/adapters/scsi_33C93_hdw.c | 2078 |
1 files changed, 2078 insertions, 0 deletions
diff --git a/scsi/adapters/scsi_33C93_hdw.c b/scsi/adapters/scsi_33C93_hdw.c new file mode 100644 index 00000000..169ccbf9 --- /dev/null +++ b/scsi/adapters/scsi_33C93_hdw.c @@ -0,0 +1,2078 @@ +/* + * 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 <sbic.h> +#if NSBIC > 0 +#include <platforms.h> + +#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 <machine/machspl.h> /* spl definitions */ +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> + +#include <scsi/adapters/scsi_33C93.h> +#include <scsi/scsi_defs.h> +#include <scsi/adapters/scsi_dma.h> + +/* + * 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 |