diff options
Diffstat (limited to 'scsi/adapters/scsi_53C700_hdw.c')
-rw-r--r-- | scsi/adapters/scsi_53C700_hdw.c | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/scsi/adapters/scsi_53C700_hdw.c b/scsi/adapters/scsi_53C700_hdw.c new file mode 100644 index 00000000..61b5a3ba --- /dev/null +++ b/scsi/adapters/scsi_53C700_hdw.c @@ -0,0 +1,696 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ +/* + * File: scsi_53C700_hdw.c + * 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 NCR 53C700 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + + +#include <siop.h> +#if NSIOP > 0 +#include <platforms.h> + +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> +#include <machine/machspl.h> + +#include <sys/syslog.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#include <scsi/adapters/scsi_53C700.h> + +#ifdef PAD +typedef struct { + volatile unsigned char siop_scntl0; /* rw: SCSI control reg 0 */ + PAD(pad0); + volatile unsigned char siop_scntl1; /* rw: SCSI control reg 1 */ + PAD(pad1); + volatile unsigned char siop_sdid; /* rw: SCSI Destination ID */ + PAD(pad2); + volatile unsigned char siop_sien; /* rw: SCSI Interrupt Enable */ + PAD(pad3); + volatile unsigned char siop_scid; /* rw: SCSI Chip ID reg */ + PAD(pad4); + volatile unsigned char siop_sxfer; /* rw: SCSI Transfer reg */ + PAD(pad5); + volatile unsigned char siop_sodl; /* rw: SCSI Output Data Latch */ + PAD(pad6); + volatile unsigned char siop_socl; /* rw: SCSI Output Control Latch */ + PAD(pad7); + volatile unsigned char siop_sfbr; /* ro: SCSI First Byte Received */ + PAD(pad8); + volatile unsigned char siop_sidl; /* ro: SCSI Input Data Latch */ + PAD(pad9); + volatile unsigned char siop_sbdl; /* ro: SCSI Bus Data Lines */ + PAD(pad10); + volatile unsigned char siop_sbcl; /* ro: SCSI Bus Control Lines */ + PAD(pad11); + volatile unsigned char siop_dstat; /* ro: DMA status */ + PAD(pad12); + volatile unsigned char siop_sstat0; /* ro: SCSI status reg 0 */ + PAD(pad13); + volatile unsigned char siop_sstat1; /* ro: SCSI status reg 1 */ + PAD(pad14); + volatile unsigned char siop_sstat2; /* ro: SCSI status reg 2 */ + PAD(pad15); + volatile unsigned char siop_res1; + PAD(pad16); + volatile unsigned char siop_res2; + PAD(pad17); + volatile unsigned char siop_res3; + PAD(pad18); + volatile unsigned char siop_res4; + PAD(pad19); + volatile unsigned char siop_ctest0; /* ro: Chip test register 0 */ + PAD(pad20); + volatile unsigned char siop_ctest1; /* ro: Chip test register 1 */ + PAD(pad21); + volatile unsigned char siop_ctest2; /* ro: Chip test register 2 */ + PAD(pad22); + volatile unsigned char siop_ctest3; /* ro: Chip test register 3 */ + PAD(pad23); + volatile unsigned char siop_ctest4; /* rw: Chip test register 4 */ + PAD(pad24); + volatile unsigned char siop_ctest5; /* rw: Chip test register 5 */ + PAD(pad25); + volatile unsigned char siop_ctest6; /* rw: Chip test register 6 */ + PAD(pad26); + volatile unsigned char siop_ctest7; /* rw: Chip test register 7 */ + PAD(pad27); + volatile unsigned char siop_temp0; /* rw: Temporary Stack reg */ + PAD(pad28); + volatile unsigned char siop_temp1; + PAD(pad29); + volatile unsigned char siop_temp2; + PAD(pad30); + volatile unsigned char siop_temp3; + PAD(pad31); + volatile unsigned char siop_dfifo; /* rw: DMA FIFO */ + PAD(pad32); + volatile unsigned char siop_istat; /* rw: Interrupt Status reg */ + PAD(pad33); + volatile unsigned char siop_res5; + PAD(pad34); + volatile unsigned char siop_res6; + PAD(pad35); + volatile unsigned char siop_dbc0; /* rw: DMA Byte Counter reg */ + PAD(pad36); + volatile unsigned char siop_dbc1; + PAD(pad37); + volatile unsigned char siop_dbc2; + PAD(pad38); + volatile unsigned char siop_dcmd; /* rw: DMA Command Register */ + PAD(pad39); + volatile unsigned char siop_dnad0; /* rw: DMA Next Address */ + PAD(pad40); + volatile unsigned char siop_dnad1; + PAD(pad41); + volatile unsigned char siop_dnad2; + PAD(pad42); + volatile unsigned char siop_dnad3; + PAD(pad43); + volatile unsigned char siop_dsp0; /* rw: DMA SCRIPTS Pointer reg */ + PAD(pad44); + volatile unsigned char siop_dsp1; + PAD(pad45); + volatile unsigned char siop_dsp2; + PAD(pad46); + volatile unsigned char siop_dsp3; + PAD(pad47); + volatile unsigned char siop_dsps0; /* rw: DMA SCRIPTS Pointer Save reg */ + PAD(pad48); + volatile unsigned char siop_dsps1; + PAD(pad49); + volatile unsigned char siop_dsps2; + PAD(pad50); + volatile unsigned char siop_dsps3; + PAD(pad51); + volatile unsigned char siop_dmode; /* rw: DMA Mode reg */ + PAD(pad52); + volatile unsigned char siop_res7; + PAD(pad53); + volatile unsigned char siop_res8; + PAD(pad54); + volatile unsigned char siop_res9; + PAD(pad55); + volatile unsigned char siop_res10; + PAD(pad56); + volatile unsigned char siop_dien; /* rw: DMA Interrupt Enable */ + PAD(pad57); + volatile unsigned char siop_dwt; /* rw: DMA Watchdog Timer */ + PAD(pad58); + volatile unsigned char siop_dcntl; /* rw: DMA Control reg */ + PAD(pad59); + volatile unsigned char siop_res11; + PAD(pad60); + volatile unsigned char siop_res12; + PAD(pad61); + volatile unsigned char siop_res13; + PAD(pad62); + volatile unsigned char siop_res14; + PAD(pad63); +} siop_padded_regmap_t; +#else +typedef siop_regmap_t siop_padded_regmap_t; +#endif + +/* + * Macros to make certain things a little more readable + */ + +/* forward decls */ + +int siop_reset_scsibus(); +boolean_t siop_probe_target(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) 53C700 interface + */ +struct siop_softc { + watchdog_t wd; + siop_padded_regmap_t *regs; /* 53C700 registers */ + scsi_dma_ops_t *dma_ops; /* DMA operations and state */ + opaque_t dma_state; + + script_t script; + int (*error_handler)(); + int in_count; /* amnt we expect to receive */ + int out_count; /* amnt we are going to ship */ + + volatile char state; +#define SIOP_STATE_BUSY 0x01 /* selecting or currently connected */ +#define SIOP_STATE_TARGET 0x04 /* currently selected as target */ +#define SIOP_STATE_COLLISION 0x08 /* lost selection attempt */ +#define SIOP_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + + scsi_softc_t *sc; + target_info_t *active_target; + + target_info_t *next_target; /* trying to seize bus */ + queue_head_t waiting_targets;/* other targets competing for bus */ + +} siop_softc_data[NSIOP]; + +typedef struct siop_softc *siop_softc_t; + +siop_softc_t siop_softc[NSIOP]; + +/* + * Definition of the controller for the auto-configuration program. + */ + +int siop_probe(), scsi_slave(), scsi_attach(), siop_go(), siop_intr(); + +caddr_t siop_std[NSIOP] = { 0 }; +struct bus_device *siop_dinfo[NSIOP*8]; +struct bus_ctlr *siop_minfo[NSIOP]; +struct bus_driver siop_driver = + { siop_probe, scsi_slave, scsi_attach, siop_go, siop_std, "rz", siop_dinfo, + "siop", siop_minfo, BUS_INTR_B4_PROBE}; + +/* + * Scripts + */ +struct script +siop_script_data_in[] = { +}, + +siop_script_data_out[] = { +}, + +siop_script_cmd[] = { +}, + +/* Synchronous transfer neg(oti)ation */ + +siop_script_try_synch[] = { +}, + +/* Disconnect sequence */ + +siop_script_disconnect[] = { +}; + + +#define DEBUG +#ifdef DEBUG + +siop_state(base) + vm_offset_t base; +{ + siop_padded_regmap_t *regs; +.... + return 0; +} +siop_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = siop_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 ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +siop_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = siop_softc[unit]->sc->target[i]; + if (tgt) + siop_target_state(tgt); + } +} + +siop_script_state(unit) +{ + script_t spt = siop_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym(spt,1); + db_printf(": %x ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(siop_softc[unit]->error_handler, 1); + return 0; + +} + +#define PRINT(x) if (scsi_debug) printf x + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + +#define TRACE + +#ifdef TRACE + +#define LOGSIZE 256 +int siop_logpt; +char siop_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x24 +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG(e,f) + char *f; +{ + siop_log[siop_logpt++] = (e); + if (siop_logpt == LOGSIZE) siop_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +siop_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = siop_logpt; i < LOGSIZE; i++) { + c = siop_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-%d", c & 0x7f); + } + db_printf("\n"); + return 0; +} + +siop_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /* TRACE */ +#define LOG(e,f) +#endif /* TRACE */ + +#else /* DEBUG */ +#define PRINT(x) +#define LOG(e,f) +#define TR(x) +#define TRCHECK +#define TRWRAP +#endif /* DEBUG */ + + +/* + * Probe/Slave/Attach functions + */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Send an identify msg to each possible target on the bus + * except of course ourselves. + */ +siop_probe(reg, ui) + char *reg; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + siop_softc_t siop = &siop_softc_data[unit]; + int target_id, i; + scsi_softc_t *sc; + register siop_padded_regmap_t *regs; + int s; + boolean_t did_banner = FALSE; + char *cmd_ptr; + static char *here = "siop_probe"; + + /* + * We are only called if the chip is there, + * but make sure anyways.. + */ + regs = (siop_padded_regmap_t *) (reg); + if (check_memory(regs, 0)) + return 0; + +#if notyet + /* Mappable version side */ + SIOP_probe(reg, ui); +#endif + + /* + * Initialize hw descriptor + */ + siop_softc[unit] = siop; + siop->regs = regs; + + if ((siop->dma_ops = (scsi_dma_ops_t *)siop_std[unit]) == 0) + /* use same as unit 0 if undefined */ + siop->dma_ops = (scsi_dma_ops_t *)siop_std[0]; + siop->dma_state = (*siop->dma_ops->init)(unit, reg); + + queue_init(&siop->waiting_targets); + + sc = scsi_master_alloc(unit, siop); + siop->sc = sc; + + sc->go = siop_go; + sc->probe = siop_probe_target; + sc->watchdog = scsi_watchdog; + siop->wd.reset = siop_reset_scsibus; + +#ifdef MACH_KERNEL + sc->max_dma_data = -1; /* unlimited */ +#else + sc->max_dma_data = scsi_per_target_virtual; +#endif + + /* + * Reset chip + */ + s = splbio(); + siop_reset(siop, TRUE); + + /* + * Our SCSI id on the bus. + */ + + sc->initiator_id = my_scsi_id(unit); + printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id); + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + */ + for (target_id = 0; target_id < 8; target_id++) { + + register unsigned csr, dsr; + scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + ..... + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + ..... + + + /* + * Found a target + */ + siop->ntargets++; + { + register target_info_t *tgt; + + tgt = scsi_slave_alloc(unit, target_id, siop); + + tgt->cmd_ptr = ... + tgt->dma_ptr = ... +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + } + } + printf(".\n"); + + splx(s); + return 1; +} + +boolean_t +siop_probe_target(sc, tgt, ior) + scsi_softc_t *sc; + target_info_t *tgt; + io_req_t ior; +{ + siop_softc_t siop = siop_softc[sc->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + + tgt->cmd_ptr = ... + tgt->dma_ptr = ... +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + + } + + if (scsi_inquiry(sc, tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + tgt->flags = TGT_ALIVE; + return TRUE; +} + + +static siop_wait(preg, until) + volatile unsigned char *preg; +{ + int timeo = 1000000; + while ((*preg & until) != until) { + delay(1); + if (!timeo--) { + printf("siop_wait TIMEO with x%x\n", *preg); + break; + } + } + return *preg; +} + + +siop_reset(siop, quickly) + siop_softc_t siop; + boolean_t quickly; +{ + register siop_padded_regmap_t *regs = siop->regs; + + .... + + if (quickly) + return; + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call siop_bus_reset(). + */ + .... + +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +siop_go(sc, tgt, cmd_count, in_count, cmd_only) + scsi_softc_t *sc; + target_info_t *tgt; + boolean_t cmd_only; +{ + siop_softc_t siop; + register int s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + + LOG(1,"go"); + + siop = (siop_softc_t)tgt->hw_state; + + .... +} + +siop_attempt_selection(siop) + siop_softc_t siop; +{ + target_info_t *tgt; + register int out_count; + siop_padded_regmap_t *regs; + register int cmd; + boolean_t ok; + scsi_ret_t ret; + + regs = siop->regs; + tgt = siop->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * Init bus state variables and set registers. + */ + siop->active_target = tgt; + + /* reselection pending ? */ + ...... +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +siop_intr(unit) +{ + register siop_softc_t siop; + register script_t scp; + register unsigned csr, bs, cmd; + register siop_padded_regmap_t *regs; + boolean_t try_match; +#if notyet + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return SIOP_intr(unit); +#endif + + LOG(5,"\n\tintr"); + + siop = siop_softc[unit]; + regs = siop->regs; + + /* ack interrupt */ + .... +} + + +siop_target_intr(siop) + register siop_softc_t siop; +{ + panic("SIOP: TARGET MODE !!!\n"); +} + +/* + * All the many little things that the interrupt + * routine might switch to + */ + +#endif /*NSIOP > 0*/ + |