diff --git a/sys/arch/sgimips/hpc/hpcdma.h b/sys/arch/sgimips/hpc/hpcdma.h index 1ce3cca5f4ce..7fe506926410 100644 --- a/sys/arch/sgimips/hpc/hpcdma.h +++ b/sys/arch/sgimips/hpc/hpcdma.h @@ -1,4 +1,4 @@ -/* $NetBSD: hpcdma.h,v 1.1 2001/08/19 03:16:21 wdk Exp $ */ +/* $NetBSD: hpcdma.h,v 1.2 2001/11/10 07:32:42 wdk Exp $ */ /* * Copyright (c) 2001 Wayne Knowles @@ -36,8 +36,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _SGIMIPS_BUS_DMA_H -#define _SGIMIPS_BUS_DMA_H +#ifndef _SGIMIPS_HPC_DMA_H +#define _SGIMIPS_HPC_DMA_H #include @@ -47,13 +47,15 @@ struct hpc_dma_softc { bus_dma_tag_t sc_dmat; u_int32_t sc_flags; -#define HPC_DMA_ACTIVE 0x80 -#define HPC_DMA_READ 0x20 +#define HPCDMA_READ 0x20 /* direction of transfer */ +#define HPCDMA_LOADED 0x40 /* bus_dmamap loaded */ +#define HPCDMA_ACTIVE 0x80 /* DMA engine is busy */ u_int32_t sc_dmacmd; int sc_ndesc; bus_dmamap_t sc_dmamap; struct hpc_dma_desc *sc_desc_kva; /* Virtual address */ struct hpc_dma_desc *sc_desc_pa; /* Physical address */ + ssize_t sc_dlen; /* number of bytes transfered */ }; @@ -62,4 +64,4 @@ void hpcdma_sglist_create(struct hpc_dma_softc *, bus_dmamap_t); void hpcdma_cntl(struct hpc_dma_softc *, u_int32_t); void hpcdma_flush(struct hpc_dma_softc *); -#endif +#endif /* _SGIMIPS_HPC_DMA_H */ diff --git a/sys/arch/sgimips/hpc/sbic.c b/sys/arch/sgimips/hpc/sbic.c index 1e5e35173119..7ac5f57dae05 100644 --- a/sys/arch/sgimips/hpc/sbic.c +++ b/sys/arch/sgimips/hpc/sbic.c @@ -1,4 +1,4 @@ -/* $NetBSD: sbic.c,v 1.1 2001/08/19 03:16:22 wdk Exp $ */ +/* $NetBSD: sbic.c,v 1.2 2001/11/10 07:32:42 wdk Exp $ */ /* * Changes Copyright (c) 2001 Wayne Knowles @@ -58,6 +58,7 @@ #include #include #include +#include #include @@ -66,11 +67,6 @@ #include #include -/* - * Since I can't find this in any other header files - */ -#define SCSI_PHASE(reg) (reg&0x07) - /* * SCSI delays * In u-seconds, primarily for state changes on the SPC. @@ -79,182 +75,387 @@ #define SBIC_DATA_WAIT 50000 /* wait per data in/out step */ #define SBIC_INIT_WAIT 50000 /* wait per step (both) during init */ +#define STATUS_UNKNOWN 0xff /* uninitialized status */ + /* * Convenience macro for waiting for a particular sbic event */ -#define SBIC_WAIT(regs, until, timeo) sbicwait(regs, until, timeo, __LINE__) +#define SBIC_WAIT(regs, until, timeo) sbic_wait(regs, until, timeo, __LINE__) -int sbicicmd __P((struct sbic_softc *, void *, int, void *, int)); -int sbicgo __P((struct sbic_softc *, struct scsipi_xfer *)); -int sbicdmaok __P((struct sbic_softc *, struct scsipi_xfer *)); -int sbicwait __P((struct sbic_softc *, u_char, int , int)); -u_char sbicselectbus __P((struct sbic_softc *)); -int sbicxfout __P((struct sbic_softc *, int, void *)); -int sbicxfin __P((struct sbic_softc *, int, void *)); -int sbicfromscsiperiod __P((struct sbic_softc *, int)); -int sbictoscsiperiod __P((struct sbic_softc *, int)); -int sbicpoll __P((struct sbic_softc *)); -int sbicnextstate __P((struct sbic_softc *, u_char, u_char)); -int sbicmsgin __P((struct sbic_softc *)); -int sbicabort __P((struct sbic_softc *, char *)); -void sbicxfdone __P((struct sbic_softc *)); -void sbicerror __P((struct sbic_softc *,u_char)); -void sbicreset __P((struct sbic_softc *)); -void sbic_scsidone __P((struct sbic_acb *, int)); -void sbic_sched __P((struct sbic_softc *)); -void sbic_dma_stop __P((struct sbic_softc *)); -void sbic_dma_setup __P((struct sbic_softc *)); +/* Convert SCSI timeout from millisecs to hz avoiding overflow */ +#define SCSI_TIMEOUT(t) ((t)>1000000 ? ((t)/1000)*hz : ((t)*hz)/1000) + +void sbic_init __P((struct sbic_softc *)); +void sbic_reset __P((struct sbic_softc *)); +int sbic_go __P((struct sbic_softc *, struct sbic_acb *)); +int sbic_dmaok __P((struct sbic_softc *, struct scsipi_xfer *)); +int sbic_wait __P((struct sbic_softc *, u_char, int , int)); +u_char sbic_selectbus __P((struct sbic_softc *, struct sbic_acb *)); +int sbic_xfout __P((struct sbic_softc *, int, void *)); +int sbic_xfin __P((struct sbic_softc *, int, void *)); +int sbic_poll __P((struct sbic_softc *, struct sbic_acb *)); +int sbic_nextstate __P((struct sbic_softc *, struct sbic_acb *, + u_char, u_char)); +int sbic_abort __P((struct sbic_softc *, struct sbic_acb *, char *)); +void sbic_xferdone __P((struct sbic_softc *)); +void sbic_error __P((struct sbic_softc *, struct sbic_acb *)); +void sbic_scsidone __P((struct sbic_softc *, struct sbic_acb *, int)); +void sbic_sched __P((struct sbic_softc *)); +void sbic_dequeue __P((struct sbic_softc *, struct sbic_acb *)); +void sbic_dma_stop __P((struct sbic_softc *)); +void sbic_dma_setup __P((struct sbic_softc *, int)); +int sbic_msgin_phase __P((struct sbic_softc *, int)); +void sbic_msgin __P((struct sbic_softc *, u_char *, int)); +void sbic_reselect __P((struct sbic_softc *, int, int, int, int)); +void sbic_sched_msgout __P((struct sbic_softc *, u_short)); +void sbic_msgout __P((struct sbic_softc *)); +void sbic_timeout __P((void *arg)); +void sbic_watchdog __P((void *arg)); +int sbic_div2stp __P((struct sbic_softc *, int)); +int sbic_stp2div __P((struct sbic_softc *, int)); +void sbic_setsync __P((struct sbic_softc *, struct sbic_tinfo *ti)); +void sbic_update_xfer_mode __P((struct sbic_softc *, int)); +void sbic_hexdump __P((u_char *, int)); + +static struct pool sbic_pool; /* Adapter Control Blocks */ +static int sbic_pool_initialized = 0; /* - * Synch xfer parameters, and timing conversions + * Timeouts */ -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_cmd_wait = SBIC_CMD_WAIT; +int sbic_cmd_wait = SBIC_CMD_WAIT; int sbic_data_wait = SBIC_DATA_WAIT; int sbic_init_wait = SBIC_INIT_WAIT; -/* - * was broken before.. now if you want this you get it for all drives - * on sbic controllers. - */ -u_char sbic_inhibit_sync[8]; -int sbic_enable_reselect = 1; /* Allow Disconnect / Reselect */ -int sbic_no_dma = 0; /* Use PIO transfers instead of DMA */ -int sbic_parallel_operations = 1; /* Allow command queues */ +int sbic_nodma = 0; /* Use polled IO transfers */ +int sbic_nodisc = 0; /* Allow command queues */ +int sbic_notags = 0; /* No Tags */ /* * Some useful stuff for debugging purposes */ #ifdef DEBUG -#define QPRINTF(a) if (sbic_debug) printf a +#define QPRINTF(a) SBIC_DEBUG(MISC, a) -int sbic_debug = 0; /* Debug all chip related things */ -int sync_debug = 0; /* Debug all Synchronous Scsi related things */ -int reselect_debug = 0; /* Debug all reselection related things */ +int sbic_debug = 0; /* Debug all chip related things */ int data_pointer_debug = 0; /* Debug Data Pointer related things */ -void sbictimeout __P((struct sbic_softc *dev)); +void sbic_print_csr __P((u_char)); #else #define QPRINTF(a) /* */ #endif +static const char *sbic_chip_names[] = SBIC_CHIP_LIST; + /* - * Save DMA pointers. Take into account partial transfer. Shut down DMA. + * Attach instance of driver and probe for sub devices */ void -sbic_dma_stop(dev) - struct sbic_softc *dev; +sbic_attach(dev) + struct sbic_softc *dev; { - struct sbic_acb* acb; - int count, - asr, - s; + struct scsipi_adapter *adapt = &dev->sc_adapter; + struct scsipi_channel *chan = &dev->sc_channel; - /* - * Only need to save pointers if DMA was active... - */ - if ((dev->sc_flags & SBICF_INDMA) == 0) - return; + adapt->adapt_dev = &dev->sc_dev; + adapt->adapt_nchannels = 1; + adapt->adapt_openings = 256; + adapt->adapt_max_periph = 256; /* Max tags per device */ + adapt->adapt_ioctl = NULL; + /* adapt_request initialized by MD interface */ + /* adapt_minphys initialized by MD interface */ - s = splbio(); + memset(chan, 0, sizeof(*chan)); + chan->chan_adapter = &dev->sc_adapter; + chan->chan_bustype = &scsi_bustype; + chan->chan_channel = 0; + chan->chan_ntargets = SBIC_NTARG; + chan->chan_nluns = SBIC_NLUN; + chan->chan_id = dev->sc_id; - /* - * Wait until WD chip is idle - */ - do { - GET_SBIC_asr(dev, asr); /* XXX */ - if (asr & SBIC_ASR_DBR) { - printf("sbic_dma_stop: asr %02x canceled!\n", asr); - splx(s); - return; - } - } while(asr & (SBIC_ASR_BSY|SBIC_ASR_CIP)); + callout_init(&dev->sc_watchdog); + dev->sc_minsync = 200/4; /* Min SCSI sync rate in 4ns units */ + dev->sc_maxoffset = SBIC_SYN_MAX_OFFSET; /* Max Sync Offset */ - /* - * Save important state. - * must be done before dmastop - */ - acb = dev->sc_nexus; - acb->sc_usedma = dev->sc_usedma; + /* + * Add reference to adapter so that we drop the reference after + * config_found() to make sure the adatper is disabled. + */ + if (scsipi_adapter_addref(&dev->sc_adapter) != 0) { + printf("%s: unable to enable controller\n", + dev->sc_dev.dv_xname); + return; + } - /* - * Fetch the residual count - */ - SBIC_TC_GET(dev, count); + dev->sc_cfflags = dev->sc_dev.dv_cfdata->cf_flags; + sbic_init(dev); - /* - * Shut down DMA - */ - dev->sc_dmastop(dev); + dev->sc_child = config_found(&dev->sc_dev, &dev->sc_channel, + scsiprint); + scsipi_adapter_delref(&dev->sc_adapter); +} - /* - * No longer in DMA - */ - dev->sc_flags &= ~SBICF_INDMA; +/* + * Initialize driver-private structures + */ +void +sbic_init(dev) + struct sbic_softc *dev; +{ + u_int i; - /* - * Ensure the WD chip is back in polled I/O mode, with nothing to - * transfer. - */ - SBIC_TC_PUT(dev, 0); - SET_SBIC_control(dev, SBIC_CTL_EDI | SBIC_CTL_IDI); + if (!sbic_pool_initialized) { + /* All instances share the same pool */ + pool_init(&sbic_pool, sizeof(struct sbic_acb), 0, 0, 0, + "sbic_acb", 0, NULL, NULL, 0); + ++sbic_pool_initialized; + } - /* - * Update current count... - */ - acb->sc_tcnt = count; + if (dev->sc_state == 0) { + TAILQ_INIT(&dev->ready_list); - /* - * Work out how many bytes were actually transferred - */ - count = dev->sc_tcnt - count; - dev->sc_tcnt = acb->sc_tcnt; + dev->sc_nexus = NULL; + dev->sc_disc = 0; + memset(dev->sc_tinfo, 0, sizeof(dev->sc_tinfo)); - /* - * Fixup partial xfers - */ - acb->sc_kv.dc_addr += count; - acb->sc_kv.dc_count -= count; + callout_reset(&dev->sc_watchdog, 60 * hz, sbic_watchdog, dev); + } else + panic("sbic: reinitializing driver!"); -#ifdef DEBUG - if (data_pointer_debug) - printf("dma_stop\n"); + dev->sc_flags = 0; + dev->sc_state = SBIC_IDLE; + sbic_reset(dev); + + for (i = 0; i < 8; i++) { + struct sbic_tinfo *ti = &dev->sc_tinfo[i]; + /* + * sc_flags = 0xTTRRSS + * + * TT = Bitmask to disable Tagged Queues + * RR = Bitmask to disable disconnect/reselect + * SS = Bitmask to diable Sync negotiation + */ + ti->flags = T_NEED_RESET; + if (dev->sc_minsync == 0 || (dev->sc_cfflags & (1<<(i+8)))) + ti->flags |= T_NOSYNC; + if (dev->sc_cfflags & (1<flags |= T_NODISC; + ti->period = dev->sc_minsync; + ti->offset = 0; + } +} + +void +sbic_reset(dev) + struct sbic_softc *dev; +{ + u_int my_id, s; + u_char csr, reg; + + SET_SBIC_cmd(dev, SBIC_CMD_ABORT); + WAIT_CIP(dev); + + s = splbio(); + + my_id = dev->sc_channel.chan_id & SBIC_ID_MASK; + if (dev->sc_clkfreq < 110) + my_id |= SBIC_ID_FS_8_10; + else if (dev->sc_clkfreq < 160) + my_id |= SBIC_ID_FS_12_15; + else if (dev->sc_clkfreq < 210) + my_id |= SBIC_ID_FS_16_20; + + /* Enable advanced features */ +#if 1 + my_id |= SBIC_ID_EAF; /* XXX - MD Layer */ #endif - splx(s); + SET_SBIC_myid(dev, my_id); + + /* Reset the chip */ + SET_SBIC_cmd(dev, SBIC_CMD_RESET); + DELAY(25); + SBIC_WAIT(dev, SBIC_ASR_INT, 0); + + /* Set up various chip parameters */ + SET_SBIC_control(dev, SBIC_CTL_EDI | SBIC_CTL_IDI); + + GET_SBIC_csr(dev, csr); /* clears interrupt also */ + GET_SBIC_cdb1(dev, dev->sc_rev); + + switch (csr) { + case 0x0: + dev->sc_chip = SBIC_CHIP_WD33C93; + break; + case 0x1: + SET_SBIC_queue_tag(dev, 0x55); + GET_SBIC_queue_tag(dev, reg); + dev->sc_chip = (reg == 0x55) ? + SBIC_CHIP_WD33C93B : SBIC_CHIP_WD33C93A; + SET_SBIC_queue_tag(dev, 0x0); + break; + default: + dev->sc_chip = SBIC_CHIP_UNKNOWN; + } + + /* + * don't allow Selection (SBIC_RID_ES) + * until we can handle target mode!! + */ + SET_SBIC_rselid(dev, SBIC_RID_ER); + + /* Asynchronous for now */ + SET_SBIC_syn(dev, 0); + + dev->sc_flags = 0; + dev->sc_state = SBIC_IDLE; + + splx(s); + + printf(": %s SCSI, rev=%d, target %d\n", + sbic_chip_names[dev->sc_chip], dev->sc_rev, + dev->sc_channel.chan_id); +} + +void +sbic_error(dev, acb) + struct sbic_softc *dev; + struct sbic_acb *acb; +{ + struct scsipi_xfer *xs = acb->xs; + + KASSERT(xs); + + if (xs->xs_control & XS_CTL_SILENT) + return; + + scsipi_printaddr(xs->xs_periph); + printf("SCSI Error\n"); +} + +/* + * Setup sync mode for given target + */ +void +sbic_setsync(dev, ti) + struct sbic_softc *dev; + struct sbic_tinfo *ti; +{ + u_char offset, period; + + if (ti->flags & T_SYNCMODE) { + offset = ti->offset; + period = sbic_stp2div(dev, ti->period); + } else { + offset = 0; + period = 0; + } + + SBIC_DEBUG(SYNC, ("sbic_setsync: sync reg = 0x%02x\n", + SBIC_SYN(offset, period))); + SET_SBIC_syn(dev, SBIC_SYN(offset, period)); +} + +/* + * Check if current operation can be done using DMA + * + * returns 1 if DMA OK, 0 for polled I/O transfer + */ +int +sbic_dmaok(dev, xs) + struct sbic_softc *dev; + struct scsipi_xfer *xs; +{ + if (sbic_nodma || (xs->xs_control & XS_CTL_POLL) || xs->datalen == 0) + return (0); + return(1); } /* * Setup for dma transfer */ void -sbic_dma_setup(dev) - struct sbic_softc *dev; +sbic_dma_setup(dev, datain) + struct sbic_softc *dev; + int datain; { struct sbic_acb *acb = dev->sc_nexus; int s; - if (acb->sc_kv.dc_count) { - s = splbio(); - dev->sc_tcnt = dev->sc_dmasetup(dev, dev->sc_dmamap, - acb->flags); - splx(s); - } + dev->sc_daddr = acb->daddr; + dev->sc_dleft = acb->dleft; + s = splbio(); + /* Indicate that we're in DMA mode */ + if (dev->sc_dleft) { + dev->sc_dmasetup(dev, &dev->sc_daddr, &dev->sc_dleft, + datain, &dev->sc_dleft); + } + splx(s); return; } /* - * used by specific sbic controller - * - * it appears that the higher level code does nothing with LUN's - * so I will too. I could plug it in, however so could they - * in scsi_scsipi_cmd(). + * Save DMA pointers. Take into account partial transfer. Shut down DMA. + */ +void +sbic_dma_stop(dev) + struct sbic_softc *dev; +{ + int count, asr; + + /* Wait until WD chip is idle */ + do { + GET_SBIC_asr(dev, asr); /* XXX */ + if (asr & SBIC_ASR_DBR) { + printf("sbic_dma_stop: asr %02x canceled!\n", asr); + break; + } + } while(asr & (SBIC_ASR_BSY|SBIC_ASR_CIP)); + + /* Only need to save pointers if DMA was active */ + if (dev->sc_flags & SBICF_INDMA) { + int s = splbio(); + + /* Shut down DMA and flush FIFO's */ + dev->sc_dmastop(dev); + + /* Fetch the residual count */ + SBIC_TC_GET(dev, count); + + /* Work out how many bytes were actually transferred */ + count = dev->sc_tcnt - count; + + if (dev->sc_dleft < count) + printf("xfer too large: dleft=%u resid=%u\n", + dev->sc_dleft, count); + + /* Fixup partial xfers */ + dev->sc_daddr += count; + dev->sc_dleft -= count; + dev->sc_tcnt = 0; + dev->sc_flags &= ~SBICF_INDMA; + splx(s); +#ifdef DEBUG + if (data_pointer_debug) + printf("dma_stop\n"); +#endif + } + /* + * Ensure the WD chip is back in polled I/O mode, with nothing to + * transfer. + */ + SBIC_TC_PUT(dev, 0); + SET_SBIC_control(dev, SBIC_CTL_EDI | SBIC_CTL_IDI); +} + + +/* + * Handle new request from scsipi layer */ void sbic_scsi_request(chan, req, arg) @@ -262,117 +463,109 @@ sbic_scsi_request(chan, req, arg) scsipi_adapter_req_t req; void *arg; { - struct scsipi_xfer *xs; - struct scsipi_periph *periph; - struct sbic_softc *dev = (void *)chan->chan_adapter->adapt_dev; - struct sbic_acb *acb; - int flags, s; + struct sbic_softc *dev = (void *)chan->chan_adapter->adapt_dev; + struct scsipi_xfer *xs; + struct scsipi_periph *periph; + struct sbic_acb *acb; + int flags, s; - switch (req) { - case ADAPTER_REQ_RUN_XFER: - xs = arg; - periph = xs->xs_periph; - flags = xs->xs_control; + switch (req) { + case ADAPTER_REQ_RUN_XFER: + xs = arg; + periph = xs->xs_periph; + flags = xs->xs_control; - if (flags & XS_CTL_DATA_UIO) - panic("sbic: scsi data uio requested"); + if (flags & XS_CTL_DATA_UIO) + panic("sbic: scsi data uio requested"); - if (dev->sc_nexus && (flags & XS_CTL_POLL)) - panic("sbic_scsicmd: busy"); + if (dev->sc_nexus && (flags & XS_CTL_POLL)) + panic("sbic_scsicmd: busy"); - s = splbio(); + s = splbio(); + acb = (struct sbic_acb *)pool_get(&sbic_pool, PR_NOWAIT); + splx(s); - if ((acb = dev->free_list.tqh_first) != NULL) - TAILQ_REMOVE(&dev->free_list, acb, chain); + if (acb == NULL) { + scsipi_printaddr(periph); + printf("cannot allocate acb\n"); + xs->error = XS_RESOURCE_SHORTAGE; + scsipi_done(xs); + return; + } - splx(s); + acb->flags = ACB_ACTIVE; + acb->xs = xs; + acb->clen = xs->cmdlen; + acb->daddr = xs->data; + acb->dleft = xs->datalen; + acb->timeout = xs->timeout; + memcpy(&acb->cmd, xs->cmd, xs->cmdlen); - if (acb == NULL) { -#ifdef DEBUG - printf("sbic_scsicmd: unable to queue request for target %d\n", - periph->periph_target); -#ifdef DDB - Debugger(); -#endif -#endif - xs->error = XS_RESOURCE_SHORTAGE; - scsipi_done(xs); - return; - } + if (flags & XS_CTL_POLL) { + /* + * Complete currently active command(s) before + * issuing an immediate command + */ + while (dev->sc_nexus) + sbic_poll(dev, dev->sc_nexus); + } - if (flags & XS_CTL_DATA_IN) - acb->flags = ACB_ACTIVE | ACB_DATAIN; - else - acb->flags = ACB_ACTIVE; + s = splbio(); + TAILQ_INSERT_TAIL(&dev->ready_list, acb, chain); + acb->flags |= ACB_READY; - acb->xs = xs; - acb->clen = xs->cmdlen; - acb->sc_kv.dc_addr = xs->data; - acb->sc_kv.dc_count = xs->datalen; - memcpy(&acb->cmd, xs->cmd, xs->cmdlen); + /* If nothing is active, try to start it now. */ + if (dev->sc_state == SBIC_IDLE) + sbic_sched(dev); + splx(s); - if (sbic_no_dma || flags & XS_CTL_POLL) { - /* - * This has major side effects -- it locks up the machine - */ - int stat; + if ((flags & XS_CTL_POLL) == 0) + return; - s = splbio(); + if (sbic_poll(dev, acb)) { + sbic_timeout(acb); + if (sbic_poll(dev, acb)) /* 2nd retry for ABORT */ + sbic_timeout(acb); + } + return; - dev->sc_flags |= SBICF_ICMD; + case ADAPTER_REQ_GROW_RESOURCES: + /* XXX Not supported. */ + return; - do { - /* - * If we already had a nexus, while away the time until idle... - * This is likely only to happen if a reselection occurs - * between here and our earlier check for ICMD && sc_nexus - * (which would have resulted in a panic() had it been true). - */ - while (dev->sc_nexus) - sbicpoll(dev); + case ADAPTER_REQ_SET_XFER_MODE: + { + struct sbic_tinfo *ti; + struct scsipi_xfer_mode *xm = arg; - /* - * Fix up the new nexus - */ - dev->sc_nexus = acb; - dev->sc_xs = xs; - dev->target = periph->periph_target; - dev->lun = periph->periph_lun; + ti = &dev->sc_tinfo[xm->xm_target]; + ti->flags &= ~(T_NEGOTIATE|T_SYNCMODE); + ti->period = 0; + ti->offset = 0; + + if ((dev->sc_cfflags & (1<<(xm->xm_target+16))) == 0 && + (xm->xm_mode & PERIPH_CAP_TQING) && !sbic_notags) + ti->flags |= T_TAG; + else + ti->flags &= ~T_TAG; - stat = sbicicmd(dev, &acb->cmd, acb->clen, - acb->sc_kv.dc_addr, acb->sc_kv.dc_count); - - } while (dev->sc_nexus != acb); - - sbic_scsidone(acb, stat); - - splx(s); - - return; - } - - s = splbio(); - TAILQ_INSERT_TAIL(&dev->ready_list, acb, chain); - - /* - * If nothing is active, try to start it now. - */ - if (dev->sc_nexus == NULL) - sbic_sched(dev); - - splx(s); - - return; - - case ADAPTER_REQ_GROW_RESOURCES: - /* XXX Not supported. */ - return; - - case ADAPTER_REQ_SET_XFER_MODE: - /* XXX Not supported. */ - return; - } + if ((xm->xm_mode & PERIPH_CAP_SYNC) != 0 && + (ti->flags & T_NOSYNC) == 0 && dev->sc_minsync != 0) { + QPRINTF(("%s: target %d: sync negotiation\n", + dev->sc_dev.dv_xname, xm->xm_target)); + ti->flags |= T_NEGOTIATE; + ti->period = dev->sc_minsync; + } + /* + * If we're not going to negotiate, send the notification + * now, since it won't happen later. + */ + if ((ti->flags & T_NEGOTIATE) == 0) + sbic_update_xfer_mode(dev, xm->xm_target); + return; + } + } } /* @@ -380,425 +573,334 @@ sbic_scsi_request(chan, req, arg) */ void sbic_sched(dev) - struct sbic_softc *dev; + struct sbic_softc *dev; { - struct scsipi_xfer *xs; - struct scsipi_periph *periph = NULL; /* Gag the compiler */ - struct sbic_acb *acb; - int flags, - stat; + struct scsipi_periph *periph = NULL; /* Gag the compiler */ + struct sbic_acb *acb; + struct sbic_tinfo *ti; + struct sbic_linfo *li; + int lun, tag, flags; - /* - * XXXSCW - * I'll keep this test here, even though I can't see any obvious way - * in which sbic_sched() could be called with sc_nexus non NULL - */ - if (dev->sc_nexus) - return; /* a command is current active */ + if (dev->sc_state != SBIC_IDLE) + return; - /* - * Loop through the ready list looking for work to do... - */ - for (acb = dev->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) { - int i, j; + KASSERT(dev->sc_nexus == NULL); - periph = acb->xs->xs_periph; - i = periph->periph_target; - j = 1 << periph->periph_lun; + /* Loop through the ready list looking for work to do... */ + TAILQ_FOREACH(acb, &dev->ready_list, chain) { + periph = acb->xs->xs_periph; + lun = periph->periph_lun; + ti = &dev->sc_tinfo[periph->periph_target]; + li = TINFO_LUN(ti, lun); - /* - * We've found a potential command, but is the target/lun busy? - */ - if ((dev->sc_tinfo[i].lubusy & j) == 0) { - /* - * Nope, it's not busy, so we can use it. - */ - dev->sc_tinfo[i].lubusy |= j; - TAILQ_REMOVE(&dev->ready_list, acb, chain); - dev->sc_nexus = acb; - break; - } - } + KASSERT(acb->flags & ACB_READY); - if (acb == NULL) { - QPRINTF(("sbicsched: no work\n")); - return; /* did not find an available command */ - } - -#ifdef DEBUG - if (data_pointer_debug) - printf("sbic_sched(%d,%d)\n", periph->periph_target, - periph->periph_lun); -#endif - - dev->sc_xs = xs = acb->xs; - flags = xs->xs_control; - - if (flags & XS_CTL_RESET) - sbicreset(dev); - - dev->sc_stat[0] = -1; - dev->target = periph->periph_target; - dev->lun = periph->periph_lun; - - if (flags & XS_CTL_POLL || (!sbic_parallel_operations && - (sbicdmaok(dev, xs) == 0))) - stat = sbicicmd(dev, &acb->cmd, acb->clen, - acb->sc_kv.dc_addr, acb->sc_kv.dc_count); - else - if (sbicgo(dev, xs) == 0 && xs->error != XS_SELTIMEOUT) - return; - else - stat = dev->sc_stat[0]; - - sbic_scsidone(acb, stat); -} - -void -sbic_scsidone(acb, stat) - struct sbic_acb *acb; - int stat; -{ - struct scsipi_xfer *xs = acb->xs; - struct scsipi_periph *periph = xs->xs_periph; - struct sbic_softc *dev = (void *)periph->periph_channel->chan_adapter->adapt_dev; - int dosched = 0; - -#ifdef DIAGNOSTIC - if (acb == NULL || xs == NULL) { - panic("sbic_scsidone -- (%d,%d) no scsipi_xfer\n", - dev->target, dev->lun); - } -#endif - -#ifdef DEBUG - if (data_pointer_debug) - printf("scsidone: (%d,%d)->(%d,%d)%02x\n", periph->periph_target, - periph->periph_lun, dev->target, dev->lun, stat); - - if (xs->xs_periph->periph_target == dev->sc_channel.chan_id) - panic("target == hostid"); -#endif - - xs->status = stat; - xs->resid = 0; /* XXXX */ - if (xs->error == XS_NOERROR) { - if (stat == SCSI_CHECK || stat == SCSI_BUSY) - xs->error = XS_BUSY; - } - - - /* - * Remove the ACB from whatever queue it's on. We have to do a bit of - * a hack to figure out which queue it's on. Note that it is *not* - * necessary to cdr down the ready queue, but we must cdr down the - * nexus queue and see if it's there, so we can mark the unit as no - * longer busy. This code is sickening, but it works. - */ - if (acb == dev->sc_nexus) { - - dev->sc_nexus = NULL; - dev->sc_xs = NULL; - - dev->sc_tinfo[periph->periph_target].lubusy &= - ~(1 << periph->periph_lun); - - if (dev->ready_list.tqh_first) - dosched = 1; /* start next command */ - - } else - if (dev->ready_list.tqh_last == &acb->chain.tqe_next) { - TAILQ_REMOVE(&dev->ready_list, acb, chain); - } else { - struct sbic_acb *a; - - for (a = dev->nexus_list.tqh_first; a; a = a->chain.tqe_next) { - if (a == acb) { - TAILQ_REMOVE(&dev->nexus_list, acb, chain); - dev->sc_tinfo[periph->periph_target].lubusy &= - ~(1 << periph->periph_lun); - break; - } - } - - if (a == NULL) { - if (acb->chain.tqe_next) - TAILQ_REMOVE(&dev->ready_list, acb, chain); + /* Select type of tag for this command */ + if ((ti->flags & T_NODISC) != 0) + tag = 0; + else if ((ti->flags & T_TAG) == 0) + tag = 0; + else if ((acb->flags & ACB_SENSE) != 0) + tag = 0; + else if (acb->xs->xs_control & XS_CTL_POLL) + tag = 0; /* No tags for polled commands */ else - panic("%s: can't find matching acb\n", - dev->sc_dev.dv_xname); + tag = acb->xs->xs_tag_type; + + if (li == NULL) { + /* Initialize LUN info and add to list. */ + li = malloc(sizeof(*li), M_DEVBUF, M_NOWAIT); + if (li == NULL) + continue; + memset(li, 0, sizeof(*li)); + li->lun = lun; + if (lun < SBIC_NLUN) + ti->lun[lun] = li; + } + li->last_used = time.tv_sec; + + /* + * We've found a potential command, but is the target/lun busy? + */ + + if (tag == 0 && li->untagged == NULL) + li->untagged = acb; /* Issue untagged */ + + if (li->untagged != NULL) { + tag = 0; + if ((li->state != L_STATE_BUSY) && li->used == 0) { + /* Issue this untagged command now */ + acb = li->untagged; + periph = acb->xs->xs_periph; + } else /* Not ready yet */ + continue; + } + + acb->tag_type = tag; + if (tag != 0) { + if (li->queued[acb->xs->xs_tag_id]) + printf("queueing to active tag\n"); + li->queued[acb->xs->xs_tag_id] = acb; + acb->tag_id = acb->xs->xs_tag_id; + li->used++; + break; + } + if (li->untagged != NULL && (li->state != L_STATE_BUSY)) { + li->state = L_STATE_BUSY; + break; + } + if (li->untagged == NULL && tag != 0) { + break; + } else + printf("%d:%d busy\n", periph->periph_target, + periph->periph_lun); } - } - /* - * Put it on the free list. - */ - acb->flags = ACB_FREE; - TAILQ_INSERT_HEAD(&dev->free_list, acb, chain); - - dev->sc_tinfo[periph->periph_target].cmds++; - - scsipi_done(xs); - - if (dosched) - sbic_sched(dev); -} - -/* - * Check if current operation can be done using DMA - * - * returns 1 if DMA OK - */ -int -sbicdmaok(dev, xs) - struct sbic_softc *dev; - struct scsipi_xfer *xs; -{ - if (sbic_no_dma || xs->datalen == 0) - return (0); - return(1); - - if (sbic_no_dma || xs->datalen == 0 || - xs->datalen & 0x03 || (int)xs->data & 0x03) - return(0); - - return(1); -} - -int -sbicwait(dev, until, timeo, line) - struct sbic_softc *dev; - u_char until; - int timeo; - int line; -{ - u_char val; - - if (timeo == 0) - timeo = 1000000; /* some large value.. */ - - GET_SBIC_asr(dev, val); - - while ((val & until) == 0) { - - if (timeo-- == 0) { - int csr; - GET_SBIC_csr(dev, csr); - printf("sbicwait TIMEO @%d with asr=x%x csr=x%x\n", - line, val, csr); -#if defined(DDB) && defined(DEBUG) - Debugger(); -#endif - return(val); /* Maybe I should abort */ - break; - } - - DELAY(1); - GET_SBIC_asr(dev, val); - } - - return(val); -} - -int -sbicabort(dev, where) - struct sbic_softc *dev; - char *where; -{ - u_char csr, - asr; - - GET_SBIC_asr(dev, asr); - GET_SBIC_csr(dev, csr); - - printf ("%s: abort %s: csr = 0x%02x, asr = 0x%02x\n", - dev->sc_dev.dv_xname, where, csr, asr); - - /* - * Clean up chip itself - */ - if (dev->sc_flags & SBICF_SELECTED) { - - while (asr & SBIC_ASR_DBR) { - /* - * sbic is jammed w/data. need to clear it - * But we don't know what direction it needs to go - */ - GET_SBIC_data(dev, asr); - printf("%s: abort %s: clearing data buffer 0x%02x\n", - dev->sc_dev.dv_xname, where, asr); - GET_SBIC_asr(dev, asr); - if (asr & SBIC_ASR_DBR) /* Not the read direction, then */ - SET_SBIC_data(dev, asr); - GET_SBIC_asr(dev, asr); - } - - WAIT_CIP(dev); - - printf("%s: sbicabort - sending ABORT command\n", dev->sc_dev.dv_xname); - SET_SBIC_cmd(dev, SBIC_CMD_ABORT); - WAIT_CIP(dev); - - GET_SBIC_asr(dev, asr); - - if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) { - /* - * ok, get more drastic.. - */ - printf("%s: sbicabort - asr %x, trying to reset\n", - dev->sc_dev.dv_xname, asr); - sbicreset(dev); - dev->sc_flags &= ~SBICF_SELECTED; - return SBIC_STATE_ERROR; - } - - printf("%s: sbicabort - sending DISC command\n", dev->sc_dev.dv_xname); - SET_SBIC_cmd(dev, SBIC_CMD_DISC); - - do { - SBIC_WAIT (dev, SBIC_ASR_INT, 0); - GET_SBIC_asr(dev, asr); - GET_SBIC_csr(dev, csr); - QPRINTF(("csr: 0x%02x, asr: 0x%02x\n", csr, asr)); - } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1) && - (csr != SBIC_CSR_CMD_INVALID)); - - /* - * lets just hope it worked.. - */ - dev->sc_flags &= ~SBICF_SELECTED; - } - - return SBIC_STATE_ERROR; -} - - -/* - * Initialize driver-private structures - */ -void -sbicinit(dev) - struct sbic_softc *dev; -{ - u_int i; - - if ((dev->sc_flags & SBICF_ALIVE) == 0) { - - struct sbic_acb *acb; - - TAILQ_INIT(&dev->ready_list); - TAILQ_INIT(&dev->nexus_list); - TAILQ_INIT(&dev->free_list); - callout_init(&dev->sc_timo_ch); - - dev->sc_nexus = NULL; - dev->sc_xs = NULL; - - acb = dev->sc_acb; - memset(acb, 0, sizeof(dev->sc_acb)); - - for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) { - TAILQ_INSERT_TAIL(&dev->free_list, acb, chain); - acb++; - } - - memset(dev->sc_tinfo, 0, sizeof(dev->sc_tinfo)); + if (acb == NULL) { + QPRINTF(("sbicsched: no work\n")); + return; /* did not find an available command */ + } #ifdef DEBUG - /* - * make sure timeout is really not needed - */ - callout_reset(&dev->sc_timo_ch, 30 * hz, (void *)sbictimeout, dev); + if (data_pointer_debug) + printf("sbic_sched(%d,%d)\n", periph->periph_target, + periph->periph_lun); #endif - } else - panic("sbic: reinitializing driver!"); + TAILQ_REMOVE(&dev->ready_list, acb, chain); + acb->flags &= ~ACB_READY; - dev->sc_flags |= SBICF_ALIVE; - dev->sc_flags &= ~SBICF_SELECTED; + flags = acb->xs->xs_control; + if (flags & XS_CTL_RESET) + sbic_reset(dev); - /* - * initialize inhibit array - * Never enable Sync, since it just doesn't work on mvme147 :( - */ - for (i = 0; i < 8; ++i) - sbic_inhibit_sync[i] = 1; + /* XXX - Implicitly call scsidone on select timeout */ + if (sbic_go(dev, acb) != 0 || acb->xs->error == XS_SELTIMEOUT) { + acb->dleft = dev->sc_dleft; + sbic_scsidone(dev, acb, dev->sc_status); + return; + } - sbicreset(dev); + return; } void -sbicreset(dev) - struct sbic_softc *dev; +sbic_scsidone(dev, acb, status) + struct sbic_softc *dev; + struct sbic_acb *acb; + int status; { - u_int my_id, - s; - u_char csr; - - s = splbio(); - - my_id = dev->sc_channel.chan_id & SBIC_ID_MASK; - - if (dev->sc_clkfreq < 110) - my_id |= SBIC_ID_FS_8_10; - else if (dev->sc_clkfreq < 160) - my_id |= SBIC_ID_FS_12_15; - else if (dev->sc_clkfreq < 210) - my_id |= SBIC_ID_FS_16_20; - - SET_SBIC_myid(dev, my_id); - - /* - * Reset the chip - */ - SET_SBIC_cmd(dev, SBIC_CMD_RESET); - DELAY(25); - - SBIC_WAIT(dev, SBIC_ASR_INT, 0); - GET_SBIC_csr(dev, csr); /* clears interrupt also */ - - /* - * Set up various chip parameters - */ - SET_SBIC_control(dev, SBIC_CTL_EDI | SBIC_CTL_IDI); - - /* - * don't allow Selection (SBIC_RID_ES) - * until we can handle target mode!! - */ - SET_SBIC_rselid(dev, SBIC_RID_ER); - - /* - * Asynchronous for now - */ - SET_SBIC_syn(dev, 0); - - /* - * Anything else was zeroed by reset - */ - splx(s); - - dev->sc_flags &= ~SBICF_SELECTED; -} - -void -sbicerror(dev, csr) - struct sbic_softc *dev; - u_char csr; -{ - struct scsipi_xfer *xs = dev->sc_xs; + struct scsipi_xfer *xs = acb->xs; + struct scsipi_periph *periph = xs->xs_periph; + struct sbic_tinfo *ti; + struct sbic_linfo *li; + int s; #ifdef DIAGNOSTIC - if (xs == NULL) - panic("sbicerror: dev->sc_xs == NULL"); + KASSERT(dev->target == periph->periph_target); + KASSERT(dev->lun == periph->periph_lun); + if (acb == NULL || xs == NULL) { + panic("sbic_scsidone -- (%d,%d) no scsipi_xfer\n", + dev->target, dev->lun); + } + KASSERT(acb->flags != ACB_FREE); #endif - if (xs->xs_control & XS_CTL_SILENT) - return; +#ifdef DEBUG + if (data_pointer_debug) + printf("scsidone: (%d,%d)->(%d,%d)%02x\n", + periph->periph_target, periph->periph_lun, + dev->target, dev->lun, status); +#endif + callout_stop(&xs->xs_callout); - printf("%s: csr == 0x%02x\n", dev->sc_dev.dv_xname, csr); + xs->status = status & SCSI_STATUS_MASK; + xs->resid = acb->dleft; + + if (xs->error == XS_NOERROR) { + switch (xs->status) { + case SCSI_CHECK: + case SCSI_TERMINATED: + /* XXX Need to read sense - return busy for now */ + /*FALLTHROUGH*/ + case SCSI_QUEUE_FULL: + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + } + } + + ti = &dev->sc_tinfo[dev->target]; + li = TINFO_LUN(ti, dev->lun); + ti->cmds++; + if (xs->error == XS_SELTIMEOUT) { + /* Selection timeout -- discard this LUN if empty */ + if (li->untagged == NULL && li->used == 0) { + if (dev->lun < SBIC_NLUN) + ti->lun[dev->lun] = NULL; + free(li, M_DEVBUF); + } + } + + sbic_dequeue(dev, acb); + if (dev->sc_nexus == acb) { + dev->sc_state = SBIC_IDLE; + dev->sc_nexus = NULL; + dev->sc_flags = 0; + + if (!TAILQ_EMPTY(&dev->ready_list)) + sbic_sched(dev); + } + + /* place control block back on free list. */ + s = splbio(); + acb->flags = ACB_FREE; + pool_put(&sbic_pool, (void *)acb); + splx(s); + + scsipi_done(xs); } +void +sbic_dequeue(dev, acb) + struct sbic_softc *dev; + struct sbic_acb *acb; +{ + struct sbic_tinfo *ti = &dev->sc_tinfo[acb->xs->xs_periph->periph_target]; + struct sbic_linfo *li; + u_int32_t lun = acb->xs->xs_periph->periph_lun; + + li = TINFO_LUN(ti, lun); +#ifdef DIAGNOSTIC + if (li == NULL || li->lun != lun) + panic("sbic_dequeue: lun %x for ecb %p does not exist\n", + lun, acb); +#endif + if (li->untagged == acb) { + li->state = L_STATE_IDLE; + li->untagged = NULL; + } + if (acb->tag_type && li->queued[acb->tag_id] != NULL) { +#ifdef DIAGNOSTIC + if (li->queued[acb->tag_id] != NULL && + (li->queued[acb->tag_id] != acb)) + panic("sbic_dequeue: slot %d for lun %x has %p " + "instead of acb %p\n", acb->tag_id, + lun, li->queued[acb->tag_id], acb); +#endif + li->queued[acb->tag_id] = NULL; + li->used--; + } +} + + +int +sbic_wait(dev, until, timeo, line) + struct sbic_softc *dev; + u_char until; + int timeo; + int line; +{ + u_char val; + + if (timeo == 0) + timeo = 1000000; /* some large value.. */ + GET_SBIC_asr(dev, val); + while ((val & until) == 0) { + if (timeo-- == 0) { + int csr; + GET_SBIC_csr(dev, csr); + printf("sbic_wait: TIMEO @%d with asr=x%x csr=x%x\n", + line, val, csr); +#if defined(DDB) && defined(DEBUG) + Debugger(); +#endif + return(val); /* Maybe I should abort */ + break; + } + DELAY(1); + GET_SBIC_asr(dev, val); + } + return(val); +} + +int +sbic_abort(dev, acb, where) + struct sbic_softc *dev; + struct sbic_acb *acb; + char *where; +{ + u_char csr, asr; + + GET_SBIC_asr(dev, asr); + GET_SBIC_csr(dev, csr); + + scsipi_printaddr(acb->xs->xs_periph); + printf ("ABORT in %s: csr=0x%02x, asr=0x%02x\n", where, csr, asr); + + acb->timeout = SBIC_ABORT_TIMEOUT; + acb->flags |= ACB_ABORT; + + /* + * Clean up chip itself + */ + if (dev->sc_nexus == acb) { + /* Reschedule timeout. */ + callout_reset(&acb->xs->xs_callout, SCSI_TIMEOUT(acb->timeout), + sbic_timeout, acb); + + while (asr & SBIC_ASR_DBR) { + /* + * sbic is jammed w/data. need to clear it + * But we don't know what direction it needs to go + */ + GET_SBIC_data(dev, asr); + printf("abort %s: clearing data buffer 0x%02x\n", + where, asr); + GET_SBIC_asr(dev, asr); + if (asr & SBIC_ASR_DBR) /* Not the read direction */ + SET_SBIC_data(dev, asr); + GET_SBIC_asr(dev, asr); + } + + scsipi_printaddr(acb->xs->xs_periph); + printf("sending ABORT command\n"); + + WAIT_CIP(dev); + SET_SBIC_cmd(dev, SBIC_CMD_ABORT); + WAIT_CIP(dev); + + GET_SBIC_asr(dev, asr); + + scsipi_printaddr(acb->xs->xs_periph); + if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) { + /* + * ok, get more drastic.. + */ + printf("Resetting bus\n"); + sbic_reset(dev); + } else { + printf("sending DISCONNECT to target\n"); + SET_SBIC_cmd(dev, SBIC_CMD_DISC); + WAIT_CIP(dev); + + do { + SBIC_WAIT (dev, SBIC_ASR_INT, 0); + GET_SBIC_asr(dev, asr); + GET_SBIC_csr(dev, csr); + QPRINTF(("csr: 0x%02x, asr: 0x%02x\n", + csr, asr)); + } while ((csr != SBIC_CSR_DISC) && + (csr != SBIC_CSR_DISC_1) && + (csr != SBIC_CSR_CMD_INVALID)); + } + dev->sc_state = SBIC_ERROR; + dev->sc_flags = 0; + } + return SBIC_STATE_ERROR; +} + + /* * select the bus, return when selected or error. * @@ -807,214 +909,167 @@ sbicerror(dev, csr) * If the return value is 0, some error happened. */ u_char -sbicselectbus(dev) - struct sbic_softc *dev; +sbic_selectbus(dev, acb) + struct sbic_softc *dev; + struct sbic_acb *acb; { - u_char target = dev->target, - lun = dev->lun, - asr, - csr, - id; + struct scsipi_xfer *xs = acb->xs; + struct sbic_tinfo *ti; + u_char target, lun, asr, csr, id; - /* - * if we're already selected, return (XXXX panic maybe?) - */ - if (dev->sc_flags & SBICF_SELECTED) - return(0); + KASSERT(dev->sc_state == SBIC_IDLE); - QPRINTF(("sbicselectbus %d: ", target)); + target = xs->xs_periph->periph_target; + lun = xs->xs_periph->periph_lun; + ti = &dev->sc_tinfo[target]; - /* - * issue select - */ - SET_SBIC_selid(dev, target); - SET_SBIC_timeo(dev, SBIC_TIMEOUT(250, dev->sc_clkfreq)); + dev->sc_state = SBIC_SELECTING; + dev->target = target; + dev->lun = lun; - GET_SBIC_asr(dev, asr); + QPRINTF(("sbic_selectbus %d: ", target)); - if (asr & (SBIC_ASR_INT|SBIC_ASR_BSY)) { - /* - * This means we got ourselves reselected upon - */ - QPRINTF(("WD busy (reselect?) ASR=%02x\n", asr)); - return 0; - } + if ((xs->xs_control & XS_CTL_POLL) == 0) + callout_reset(&xs->xs_callout, SCSI_TIMEOUT(acb->timeout), + sbic_timeout, acb); - SET_SBIC_cmd(dev, SBIC_CMD_SEL_ATN); + /* + * issue select + */ + SBIC_TC_PUT(dev, 0); + SET_SBIC_selid(dev, target); + SET_SBIC_timeo(dev, SBIC_TIMEOUT(250, dev->sc_clkfreq)); - /* - * wait for select (merged from seperate function may need - * cleanup) - */ - WAIT_CIP(dev); + GET_SBIC_asr(dev, asr); + if (asr & (SBIC_ASR_INT|SBIC_ASR_BSY)) { + /* This means we got ourselves reselected upon */ + QPRINTF(("WD busy (reselect?) ASR=%02x\n", asr)); + return 0; + } - do { + SET_SBIC_cmd(dev, SBIC_CMD_SEL_ATN); + WAIT_CIP(dev); - asr = SBIC_WAIT(dev, SBIC_ASR_INT | SBIC_ASR_LCI, 0); + /* + * wait for select (merged from seperate function may need + * cleanup) + */ + do { + asr = SBIC_WAIT(dev, SBIC_ASR_INT | SBIC_ASR_LCI, 0); + if (asr & SBIC_ASR_LCI) { + QPRINTF(("late LCI: asr %02x\n", asr)); + return 0; + } - if (asr & SBIC_ASR_LCI) { - QPRINTF(("late LCI: asr %02x\n", asr)); - return 0; + /* Clear interrupt */ + GET_SBIC_csr (dev, csr); + + /* Reselected from under our feet? */ + if (csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) { + QPRINTF(("got reselected, asr %02x\n", asr)); + /* + * We need to handle this now so we don't lock up later + */ + sbic_nextstate(dev, acb, csr, asr); + return 0; + } + + /* Whoops! */ + if (csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN) { + panic("sbic_selectbus: target issued select!"); + return 0; + } + + } while (csr != (SBIC_CSR_MIS_2 | MESG_OUT_PHASE) && + csr != (SBIC_CSR_MIS_2 | CMD_PHASE) && + csr != SBIC_CSR_SEL_TIMEO); + + /* Anyone at home? */ + if (csr == SBIC_CSR_SEL_TIMEO) { + xs->error = XS_SELTIMEOUT; + QPRINTF(("-- Selection Timeout\n")); + return 0; + } + + QPRINTF(("Selection Complete\n")); + + /* Assume we're now selected */ + GET_SBIC_selid(dev, id); + if (id != target) { + /* Something went wrong - wrong target was select */ + printf("sbic_selectbus: wrong target selected;" + " WANTED %d GOT %d", target, id); + return 0; /* XXX: Need to call nexstate to handle? */ + } + + dev->sc_flags |= SBICF_SELECTED; + dev->sc_state = SBIC_CONNECTED; + + /* setup correct sync mode for this target */ + sbic_setsync(dev, ti); + + if (ti->flags & T_NODISC && dev->sc_disc == 0) + SET_SBIC_rselid (dev, 0); /* Not expecting a reselect */ + else + SET_SBIC_rselid (dev, SBIC_RID_ER); + + /* + * We only really need to do anything when the target goes to MSG out + * If the device ignored ATN, it's probably old and brain-dead, + * but we'll try to support it anyhow. + * If it doesn't support message out, it definately doesn't + * support synchronous transfers, so no point in even asking... + */ + if (csr == (SBIC_CSR_MIS_2 | MESG_OUT_PHASE)) { + if (ti->flags & T_NEGOTIATE) { + /* Inititae a SDTR message */ + SBIC_DEBUG(SYNC, ("Sending SDTR to target %d\n", id)); + ti->period = dev->sc_minsync; + ti->offset = dev->sc_maxoffset; + + /* Send Sync negotiation message */ + dev->sc_omsg[0] = MSG_IDENTIFY(lun, 0); /* No Disc */ + dev->sc_omsg[1] = MSG_EXTENDED; + dev->sc_omsg[2] = MSG_EXT_SDTR_LEN; + dev->sc_omsg[3] = MSG_EXT_SDTR; + dev->sc_omsg[4] = dev->sc_minsync; + dev->sc_omsg[5] = dev->sc_maxoffset; + sbic_xfout(dev, 6, dev->sc_omsg); + dev->sc_msgout |= SEND_SDTR; /* may be rejected */ + dev->sc_flags |= SBICF_SYNCNEGO; + } else { + if (dev->sc_nexus->tag_type != 0) { + /* Use TAGS */ + SBIC_DEBUG(TAGS, ("