diff options
Diffstat (limited to 'sys/dev/aic7xxx/aic7xxx.seq')
-rw-r--r-- | sys/dev/aic7xxx/aic7xxx.seq | 716 |
1 files changed, 414 insertions, 302 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq index f67069d..cbdd497 100644 --- a/sys/dev/aic7xxx/aic7xxx.seq +++ b/sys/dev/aic7xxx/aic7xxx.seq @@ -39,7 +39,7 @@ * *-M************************************************************************/ -VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.42 1996/06/09 17:29:11 gibbs Exp $" +VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.42.2.2 1996/10/06 01:24:09 gibbs Exp $" #if defined(__NetBSD__) #include "../../../../dev/ic/aic7xxxreg.h" @@ -57,7 +57,9 @@ VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.42 1996/06/09 17:29:11 gibbs Exp $ */ A = ACCUM -/* After starting the selection hardware, we check for reconnecting targets +/* + * A few words on the waiting SCB list: + * After starting the selection hardware, we check for reconnecting targets * as well as for our selection to complete just in case the reselection wins * bus arbitration. The problem with this is that we must keep track of the * SCB that we've already pulled from the QINFIFO and started the selection @@ -66,10 +68,11 @@ A = ACCUM * in scratch ram since a reconnecting target can request sense and this will * create yet another SCB waiting for selection. The solution used here is to * use byte 27 of the SCB as a psuedo-next pointer and to thread a list - * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets, - * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must - * add an entry to this list everytime a request sense occurs. The sequencer - * will automatically consume the entries. + * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB indexes, + * SCB_LIST_NULL is 0xff which is out of range. An entry is also added to + * this list everytime a request sense occurs or after completing a non-tagged + * command for which a second SCB has been queued. The sequencer will + * automatically consume the entries. */ /* @@ -83,7 +86,7 @@ reset: * We jump to start after every bus free. */ start: - and FLAGS,0x0f /* clear target specific flags */ + and FLAGS,0x07 /* clear target specific flags */ mvi SCSISEQ,ENRSELI /* Always allow reselection */ clr SCSIRATE /* * We don't know the target we will @@ -103,65 +106,88 @@ poll_for_work: xor SBLKCTL,SELBUSB /* Toggle to the original bus */ start2: test SSTAT0,SELDI jnz reselect - cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting + cmp WAITING_SCBH,SCB_LIST_NULL je test_queue +start_waiting: + /* + * Pull the first entry off of the waiting SCB list + * We don't have to "test_busy" because only transactions that + * have passed that test can be in the WAITING_SCB list. + */ + mov SCBPTR,WAITING_SCBH + jmp start_scb2 +test_queue: + /* Has the driver posted any work for us? */ mov A, QCNTMASK test QINCNT,A jz poll_for_work /* * We have at least one queued SCB now and we don't have any - * SCBs in the list of SCBs awaiting selection. Set the SCB - * pointer from the FIFO so we see the right bank of SCB - * registers. + * SCBs in the list of SCBs awaiting selection. If we have + * any SCBs availible for use, pull the tag from the QINFIFO + * and get to work on it. */ - mov SCBPTR,QINFIFO + test FLAGS, PAGESCBS jz dequeue_scb + call get_free_or_disc_scb + cmp SINDEX, SCB_LIST_NULL je poll_for_work +dequeue_scb: + mov CUR_SCBID,QINFIFO + test FLAGS, PAGESCBS jnz dma_queued_scb + /* In the non-paging case, the SCBID == hardware SCB index */ + mov SCBPTR, CUR_SCBID +dma_queued_scb: +/* + * DMA the SCB from host ram into the current SCB location. + */ + mvi DMAPARAMS, 0xd /* HDMAEN|DIRECTION|FIFORESET */ + mov CUR_SCBID call dma_scb /* * See if there is not already an active SCB for this target. This code * locks out on a per target basis instead of target/lun. Although this * is not ideal for devices that have multiple luns active at the same * time, it is faster than looping through all SCB's looking for active - * commands. It may be benificial to make findscb a more general procedure - * to see if the added cost of the search is negligible. This code also - * assumes that the kernel driver will clear the active flags on board - * initialization, board reset, and a target SELTO. Tagged commands - * don't set the active bits since you can queue more than one command - * at a time. We do, however, look to see if there are any non-tagged - * I/Os in progress, and requeue the command if there are. Tagged and - * non-tagged commands cannot be mixed to a single target. + * commands. We also don't have enough spare SCB space for to store the + * SCBID of the currently busy transaction for each target/lun making it + * impossible to link up the SCBs. */ - test_busy: - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */ - - test ACTIVE_B,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_B,A - jmp start_scb - -/* Place the currently active SCB back on the queue for later processing */ -requeue: - mov QINFIFO, SCBPTR + test SCB_CONTROL, TAG_ENB jnz start_scb + mov SAVED_SCBPTR, SCBPTR + call index_untagged_scb + mov ARG_1, SINDIR /* + * ARG_1 should + * now have the SCB ID of + * any active, non-tagged, + * command for this target. + */ + cmp ARG_1, SCB_LIST_NULL je make_busy + test FLAGS, PAGESCBS jz simple_busy_link + /* + * Put this SCB back onto the free list. It + * may be necessary to satisfy the search for + * the active SCB. + */ + call add_scb_to_free_list + /* Find the active SCB */ + mov ALLZEROS call findSCB + /* Link us in */ + mov SCB_LINKED_NEXT, CUR_SCBID + /* Put it back on the disconnected list */ + call add_scb_to_disc_list jmp poll_for_work - -/* - * Pull the first entry off of the waiting for selection list - * We don't have to "test_busy" because only transactions that - * have passed that test can be in the waiting_scb list. - */ -start_waiting: - mov SCBPTR,WAITING_SCBH - jmp start_scb2 - -test_a: - test ACTIVE_A,A jnz requeue - test SCB_CONTROL,TAG_ENB jnz start_scb - /* Mark the current target as busy */ - or ACTIVE_A,A +simple_busy_link: + mov SCBPTR, ARG_1 + mov SCB_LINKED_NEXT, CUR_SCBID + jmp poll_for_work +make_busy: + mov DINDIR, CUR_SCBID + mov SCBPTR, SAVED_SCBPTR start_scb: + /* + * Place us on the waiting list in case our selection + * doesn't win during bus arbitration. + */ mov SCB_NEXT,WAITING_SCBH mov WAITING_SCBH, SCBPTR start_scb2: @@ -190,9 +216,8 @@ start_selection: */ mk_identify: - and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ - and MSG0,0x7,SCB_TCL /* lun */ + and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ or MSG0,A /* or in disconnect privledge */ or MSG0,MSG_IDENTIFYFLAG mvi MSG_LEN, 1 @@ -203,11 +228,9 @@ mk_identify: */ mk_tag: test SCB_CONTROL,TAG_ENB jz mk_message - mvi DINDEX, MSG1 - and DINDIR,0x23,SCB_CONTROL - mov DINDIR,SCB_TAG - - add MSG_LEN,COMP_MSG0,DINDEX /* update message length */ + and MSG1,0x23,SCB_CONTROL + mov MSG2,SCB_TAG + add MSG_LEN,2 /* update message length */ /* * Interrupt the driver, and allow it to tweak the message buffer @@ -224,8 +247,7 @@ wait_for_selection: /* * Reselection has been initiated by a target. Make a note that we've been - * reselected, but haven't seen an IDENTIFY message from the target - * yet. + * reselected, but haven't seen an IDENTIFY message from the target yet. */ reselect: clr MSG_LEN /* Don't have anything in the mesg buffer */ @@ -234,43 +256,51 @@ reselect: jmp select2 /* - * After the selection, remove this SCB from the "waiting for selection" + * After the selection, remove this SCB from the "waiting SCB" * list. This is achieved by simply moving our "next" pointer into * WAITING_SCBH. Our next pointer will be set to null the next time this * SCB is used, so don't bother with it now. */ select: mov WAITING_SCBH,SCB_NEXT - or FLAGS,SELECTED select2: /* - * Set CLRCHN here before the target has entered a data transfer mode - - * with synchronous SCSI, if you do it later, you blow away some - * data in the SCSI FIFO that the target has already sent to you. - */ - or SXFRCTL0,CLRCHN -/* * Initialize SCSIRATE with the appropriate value for this target. + * The SCSIRATE settings for each target are stored in an array + * based at TARG_SCRATCH. */ - call ndx_dtr +ndx_dtr: + shr A,SCSIID,4 + test SBLKCTL,SELBUSB jz ndx_dtr_2 + or SAVED_TCL, SELBUSB /* Add the channel bit while we're here */ + or A,0x08 /* Channel B entries add 8 */ +ndx_dtr_2: + add SINDEX,TARG_SCRATCH,A mov SCSIRATE,SINDIR /* - * Initialize Ultra mode setting. + * Initialize Ultra mode setting and clear the SCSI channel. */ - mov FUNCTION1,SCSIID - mov A,FUNCTION1 - and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ - test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */ - test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */ - test ULTRA_ENB,A jz set_sxfrctl0 - or SINDEX, ULTRAEN jmp set_sxfrctl0 -ultra_b: - test ULTRA_ENB_B,A jz set_sxfrctl0 - or SINDEX, ULTRAEN - +ultra: + and DINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */ + /* + * Set CLRCHN here before the target has entered a data transfer mode - + * with synchronous SCSI, if you do it later, you blow away some + * data in the SCSI FIFO that the target has already sent to you. + */ + or DINDEX, CLRCHN + mvi SINDEX, ULTRA_ENB_B + test SCSIID, 0x80 jnz ultra_2 /* Target ID > 7 */ + test SBLKCTL, SELBUSB jnz ultra_2 /* Second channel device */ + dec SINDEX +ultra_2: + mov FUNCTION1,SCSIID + mov A,FUNCTION1 + test SINDIR, A jz set_sxfrctl0 + or DINDEX, ULTRAEN + set_sxfrctl0: - mov SXFRCTL0,SINDEX + mov SXFRCTL0,DINDEX mvi SCSISEQ,ENAUTOATNP /* * ATN on parity errors @@ -326,11 +356,31 @@ p_datain: * !DIRECTION|FIFORESET */ data_phase_init: - call assert + call assert /* + * Ensure entering a data + * phase is okay - seen identify, etc. + */ test FLAGS, DPHASE jnz data_phase_reinit - call sg_scb2ram - or FLAGS, DPHASE /* We have seen a data phase */ + + /* + * Initialize the DMA address and counter from the SCB. + * Also set SG_COUNT and SG_NEXT in memory since we cannot + * modify the values in the SCB itself until we see a + * save data pointers message. + */ + mvi DINDEX, HADDR0 + mvi SCB_DATAPTR call bcopy_7 + + call set_stcnt_from_hcnt + + mov SG_COUNT,SCB_SGCOUNT + + mvi DINDEX, SG_NEXT + mvi SCB_SGPTR call bcopy_4 + + /* We have seen a data phase */ + or FLAGS, DPHASE data_phase_loop: /* Guard against overruns */ @@ -347,7 +397,7 @@ data_phase_loop: mvi STCNT2,0xff data_phase_inbounds: -/* If we are the last SG block, don't set wideodd. */ +/* If we are the last SG block, ensure wideodd is off. */ cmp SG_COUNT,0x01 jne data_phase_wideodd and DMAPARAMS, 0xbf /* Turn off WIDEODD */ data_phase_wideodd: @@ -383,67 +433,27 @@ sg_load: clr HCNT1 mvi HCNT0,SG_SIZEOF - mov HADDR0,SG_NEXT0 - mov HADDR1,SG_NEXT1 - mov HADDR2,SG_NEXT2 - mov HADDR3,SG_NEXT3 + mvi DINDEX, HADDR0 + mvi SG_NEXT0 call bcopy_4 or DFCNTRL,0xd /* HDMAEN|DIRECTION|FIFORESET */ -/* - * Wait for DMA from host memory to data FIFO to complete, then disable - * DMA and wait for it to acknowledge that it's off. - */ -dma_finish: - test DFSTATUS,HDONE jz dma_finish - /* Turn off DMA preserving WIDEODD */ - and DFCNTRL,WIDEODD -dma_finish2: - test DFCNTRL,HDMAENACK jnz dma_finish2 + call dma_finish /* * Copy data from FIFO into SCB data pointer and data count. This assumes - * that the struct scatterlist has this structure (this and sizeof(struct - * scatterlist) == 12 are asserted in aic7xxx.c for the Linux driver): - * - * struct scatterlist { - * char *address; four bytes, little-endian order - * ... four bytes, ignored - * unsigned short length; two bytes, little-endian order - * } - * + * that the SG segments are of the form: * - * In FreeBSD, the scatter list entry is only 8 bytes. - * * struct ahc_dma_seg { - * physaddr addr; four bytes, little-endian order - * long len; four bytes, little endian order + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order * }; */ - - mov HADDR0,DFDAT - mov HADDR1,DFDAT - mov HADDR2,DFDAT - mov HADDR3,DFDAT -/* - * For Linux, we must throw away four bytes since there is a 32bit gap - * in the middle of a struct scatterlist. - */ -#ifdef __linux__ - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT - mov NONE,DFDAT -#endif - mov HCNT0,DFDAT - mov HCNT1,DFDAT - mov HCNT2,DFDAT + mvi HADDR0 call dfdat_in_7 /* Load STCNT as well. It is a mirror of HCNT */ - mov STCNT0,HCNT0 - mov STCNT1,HCNT1 - mov STCNT2,HCNT2 - test SSTAT1,PHASEMIS jz data_phase_loop + call set_stcnt_from_hcnt + test SSTAT1,PHASEMIS jz data_phase_loop data_phase_finish: /* @@ -474,17 +484,12 @@ p_command: /* * Load HADDR and HCNT. */ - mov HADDR0, SCB_CMDPTR0 - mov HADDR1, SCB_CMDPTR1 - mov HADDR2, SCB_CMDPTR2 - mov HADDR3, SCB_CMDPTR3 - mov HCNT0, SCB_CMDLEN + mvi DINDEX, HADDR0 + mvi SCB_CMDPTR call bcopy_5 clr HCNT1 clr HCNT2 - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 + call set_stcnt_from_hcnt mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| # DIRECTION|FIFORESET @@ -505,7 +510,6 @@ p_status: p_mesgout: test MSG_LEN, 0xff jnz p_mesgout_start mvi MSG_NOOP call mk_mesg /* build NOP message */ - p_mesgout_start: /* * Set up automatic PIO transfer from MSG0. Bit 3 in @@ -572,12 +576,9 @@ p_mesgin: rej_mesgin: /* - * We have no idea what this message in is, and there's no way - * to pass it up to the kernel, so we issue a message reject and - * hope for the best. Since we're now using manual PIO mode to - * read in the message, there should no longer be a race condition - * present when we assert ATN. In any case, rejection should be a - * rare occurrence - signal the driver when it happens. + * We have no idea what this message in is, so we issue a message reject + * and hope for the best. In any case, rejection should be a rare + * occurrence - signal the driver when it happens. */ or SCSISIGO,ATNO /* turn on ATNO */ mvi INTSTAT,SEND_REJECT /* let driver know */ @@ -591,40 +592,38 @@ mesgin_done: mesgin_complete: /* - * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT, - * and trigger a completion interrupt. Check status for non zero return - * and interrupt driver if needed. This allows the driver to interpret - * errors only when they occur instead of always uploading the scb. If - * the status is SCSI_CHECK, the driver will download a new scb requesting - * sense to replace the old one, modify the "waiting for selection" SCB list - * and set RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE the - * sequencer imediately jumps to main loop where it will run down the waiting - * SCB list and process the sense request. If the kernel driver does not - * wish to request sense, it need only clear RETURN_1, and the command is - * allowed to complete. We don't bother to post to the QOUTFIFO in the - * error case since it would require extra work in the kernel driver to - * ensure that the entry was removed before the command complete code tried - * processing it. - * - * First check for residuals + * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO, + * and trigger a completion interrupt. Before doing so, check to see if there + * is a residual or the status byte is something other than NO_ERROR (0). In + * either of these conditions, we upload the SCB back to the host so it can + * process this information. In the case of a non zero status byte, we + * additionally interrupt the kernel driver synchronously, allowing it to + * decide if sense should be retrieved. If the kernel driver wishes to request + * sense, it will fill the kernel SCB with a request sense command and set + * RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE we redownload + * the SCB, and process it as the next command by adding it to the waiting list. + * If the kernel driver does not wish to request sense, it need only clear + * RETURN_1, and the command is allowed to complete normally. We don't bother + * to post to the QOUTFIFO in the error cases since it would require extra + * work in the kernel driver to ensure that the entry was removed before the + * command complete code tried processing it. */ - test SCB_RESID_SGCNT,0xff jz check_status + /* - * If we have a residual count, interrupt and tell the host. Other - * alternatives are to pause the sequencer on all command completes (yuck), - * dma the resid directly to the host (slick, we may have space to do it now) - * or have the sequencer pause itself when it encounters a non-zero resid - * (unecessary pause just to flag the command -yuck-, but takes one instruction - * and since it shouldn't happen that often is good enough for our purposes). + * First check for residuals */ -resid: - mvi INTSTAT,RESIDUAL - -check_status: + test SCB_RESID_SGCNT,0xff jnz upload_scb test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */ +upload_scb: + mvi DMAPARAMS, 0x9 /* HDMAEN | FIFORESET*/ + mov SCB_TAG call dma_scb +check_status: + test SCB_TARGET_STATUS,0xff jz status_ok /* Just a residual? */ mvi INTSTAT,BAD_STATUS /* let driver know */ cmp RETURN_1, SEND_SENSE jne status_ok - jmp mesgin_done + /* This SCB becomes the next to execute as it will retrieve sense */ + mov SCB_LINKED_NEXT, SCB_TAG + jmp dma_next_scb status_ok: /* First, mark this target as free. */ @@ -633,14 +632,11 @@ status_ok: * don't busy the * target. */ - mov FUNCTION1,SCB_TCL - mov A,FUNCTION1 - test SCB_TCL,0x88 jz clear_a - xor ACTIVE_B,A - jmp test_immediate - -clear_a: - xor ACTIVE_A,A + mov SAVED_SCBPTR, SCBPTR + mov SAVED_LINKPTR, SCB_LINKED_NEXT + call index_untagged_scb + mov DINDIR, SAVED_LINKPTR + mov SCBPTR, SAVED_SCBPTR test_immediate: test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */ @@ -650,12 +646,33 @@ test_immediate: * with the completion of this command can occur. */ mvi INTSTAT,IMMEDDONE - jmp start + jmp dma_next_scb complete: + test FLAGS, PAGESCBS jz complete_post + /* Wait for a free slot - we may need to wait only in the paging case */ + mov A, QOUTCNT + cmp QFULLCNT, A je complete +complete_post: + /* Post the SCB and issue an interrupt */ mov QOUTFIFO,SCB_TAG mvi INTSTAT,CMDCMPLT - jmp mesgin_done +dma_next_scb: + cmp SCB_LINKED_NEXT, SCB_LIST_NULL je mesgin_done + test FLAGS, PAGESCBS jnz dma_next_scb2 + /* Only DMA on top of ourselves if we are the SCB to download */ + mov A, SCB_LINKED_NEXT + cmp SCB_TAG, A je dma_next_scb2 + mov SCBPTR, A + jmp add_to_waiting_list +dma_next_scb2: + mvi DMAPARAMS, 0xd /* HDMAEN|DIRECTION|FIFORESET */ + mov SCB_TAG call dma_scb +add_to_waiting_list: + mov SCB_NEXT,WAITING_SCBH + mov WAITING_SCBH, SCBPTR + or FLAGS, SCB_LISTED + jmp mesgin_done /* * Is it an extended message? Copy the message to our message buffer and @@ -665,26 +682,14 @@ complete: */ mesgin_extended: mvi MSGIN_EXT_LEN call inb_next - mvi MSGIN_EXT_OPCODE call inb_next mov A, MSGIN_EXT_LEN - dec A /* Length counts the op code */ - mvi SINDEX, MSGIN_EXT_BYTE0 mesgin_extended_loop: - test A, 0xFF jz mesgin_extended_intr - cmp SINDEX, MSGIN_EXT_LASTBYTE je mesgin_extended_dump - call inb_next - dec A -/* - * We pass the arg to inb in SINDEX, but DINDEX is the one incremented - * so update SINDEX with DINDEX's value before looping again. - */ - mov DINDEX jmp mesgin_extended_loop -mesgin_extended_dump: -/* We have no more storage space, so dump the rest */ - test A, 0xFF jz mesgin_extended_intr - mvi NONE call inb_next + mov DINDEX call inb_next dec A - jmp mesgin_extended_dump + cmp DINDEX, MSGIN_EXT_LASTBYTE jne mesgin_extended_loop_test + dec DINDEX /* dump by repeatedly filling the last byte */ +mesgin_extended_loop_test: + test A, 0xFF jnz mesgin_extended_loop mesgin_extended_intr: mvi INTSTAT,EXTENDED_MSG /* let driver know */ cmp RETURN_1,SEND_REJ je rej_mesgin @@ -700,29 +705,34 @@ mesgin_extended_intr: mesgin_disconnect: or SCB_CONTROL,DISCONNECTED test FLAGS, PAGESCBS jz mesgin_done -/* - * Link this SCB into the DISCONNECTED list. This list holds the - * candidates for paging out an SCB if one is needed for a new command. - * Modifying the disconnected list is a critical(pause dissabled) section. - */ - mvi SCB_PREV, SCB_LIST_NULL - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ - mov SCB_NEXT, DISCONNECTED_SCBH - mov DISCONNECTED_SCBH, SCBPTR - cmp SCB_NEXT,SCB_LIST_NULL je linkdone - mov SCBPTR,SCB_NEXT - mov SCB_PREV,DISCONNECTED_SCBH - mov SCBPTR,DISCONNECTED_SCBH -linkdone: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ + call add_scb_to_disc_list + or FLAGS, SCB_LISTED jmp mesgin_done /* - * Save data pointers message? Copy working values into the SCB, - * usually in preparation for a disconnect. + * Save data pointers message: + * Copying RAM values back to SCB, for Save Data Pointers message, but + * only if we've actually been into a data phase to change them. This + * protects against bogus data in scratch ram and the residual counts + * since they are only initialized when we go into data_in or data_out. */ mesgin_sdptrs: - call sg_ram2scb + test FLAGS, DPHASE jz mesgin_done + mov SCB_SGCOUNT,SG_COUNT + + /* The SCB SGPTR becomes the next one we'll download */ + mvi DINDEX, SCB_SGPTR + mvi SG_NEXT0 call bcopy_4 + + /* The SCB DATAPTR0 becomes the current SHADDR */ + mvi DINDEX, SCB_DATAPTR0 + mvi SHADDR0 call bcopy_4 + +/* + * Use the residual number since STCNT is corrupted by any message transfer. + */ + mvi SCB_RESID_DCNT0 call bcopy_3 + jmp mesgin_done /* @@ -745,12 +755,8 @@ mesgin_rdptrs: */ mesgin_identify: test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/ - and A,0x07 /* lun in lower three bits */ - or SAVED_TCL,A,SELID - and SAVED_TCL,0xf7 - and A,SELBUSB,SBLKCTL /* B Channel?? */ - or SAVED_TCL,A + or SAVED_TCL,A /* SAVED_TCL should be complete now */ call inb_last /* ACK */ /* @@ -784,24 +790,23 @@ get_tag: * to the reconnecting target. */ test FLAGS, PAGESCBS jz index_by_tag - call inb_last /* Ack Tag */ use_findSCB: mov ALLZEROS call findSCB /* Have to search */ setup_SCB: and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */ or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */ + test SCB_CONTROL,TAG_ENB jnz mesgin_done /* Ack Tag */ jmp ITloop index_by_tag: mov SCBPTR,ARG_1 mov A,SAVED_TCL cmp SCB_TCL,A jne abort_tag test SCB_CONTROL,TAG_ENB jz abort_tag - call inb_last /* Ack Successful tag */ jmp setup_SCB abort_tag: or SCSISIGO,ATNO /* turn on ATNO */ - mvi INTSTAT,ABORT_TAG /* let driver know */ +abort_tag_msg: mvi MSG_ABORT_TAG call mk_mesg /* ABORT TAG message */ jmp mesgin_done @@ -833,6 +838,14 @@ p_busfree: * notify the driver. */ test SCB_CMDLEN,0xff jz status_ok + test FLAGS, SCB_LISTED jnz start + /* + * This SCB didn't disconnect or have a command complete, + * so put it on the free queue. It was probably the + * result of an abort of some sort. This prevents us + * from "leaking" SCBs. + */ + call add_scb_to_free_list jmp start /* @@ -937,6 +950,7 @@ return: */ initialize_scsiid: and SINDEX,0xf0 /* Get target ID */ + mov SAVED_TCL, SINDEX /* Update the target portion of this */ and A,0x0f,SCSIID or SINDEX,A mov SCSIID,SINDEX ret @@ -949,7 +963,7 @@ assert: test FLAGS,RESELECTED jz return /* reselected? */ test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */ - mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */ + mvi INTSTAT,NO_IDENT ret /* no - tell the kernel */ /* * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag @@ -961,17 +975,42 @@ assert: findSCB: mov A,SAVED_TCL mov SCBPTR,SINDEX /* switch to next SCB */ - mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */ cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */ test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/ test SCB_CONTROL,TAG_ENB jnz findTaggedSCB cmp ARG_1,SCB_LIST_NULL je foundSCB - jmp findSCB1 +findSCB1: + inc SINDEX + mov A,SCBCOUNT + cmp SINDEX,A jne findSCB +/* + * We didn't find it. If we're paging, pull an SCB and DMA down the + * one we want. If we aren't paging or the SCB we dma down has the + * abort flag set, alert the kernel and send an abort message. + */ + test FLAGS, PAGESCBS jz find_abort + call get_free_or_disc_scb + mov CUR_SCBID, ARG_1 + cmp CUR_SCBID, SCB_LIST_NULL jne find_dma_scb + call index_untagged_scb + mov CUR_SCBID, SINDIR +find_dma_scb: + mvi DMAPARAMS, 0xd /* HDMAEN|DIRECTION|FIFORESET */ + mov CUR_SCBID call dma_scb + test SCB_CONTROL, ABORT_SCB jz return +find_abort: + mvi INTSTAT,NO_MATCH /* not found - signal kernel */ + or SCSISIGO,ATNO /* assert ATNO */ + cmp ARG_1,SCB_LIST_NULL jne abort_tag_msg + mvi MSG_ABORT call mk_mesg + jmp ITloop findTaggedSCB: mov A, ARG_1 /* Tag passed in ARG_1 */ cmp SCB_TAG,A jne findSCB1 /* Found it? */ foundSCB: - test FLAGS,PAGESCBS jz foundSCB_ret + test SCB_CONTROL, ABORT_SCB jnz find_abort + test FLAGS,PAGESCBS jz return +rem_scb_from_disc_list: /* Remove this SCB from the disconnection list */ cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev mov SAVED_LINKPTR, SCB_PREV @@ -983,87 +1022,160 @@ unlink_prev: mov SAVED_LINKPTR, SCB_NEXT mov SCBPTR, SCB_PREV mov SCB_NEXT, SAVED_LINKPTR - mov SCBPTR, SINDEX - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ + mov SCBPTR, SINDEX ret rHead: - mov DISCONNECTED_SCBH,SCB_NEXT -foundSCB_ret: - mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */ - -findSCB1: - mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */ - inc SINDEX - mov A,SCBCOUNT - cmp SINDEX,A jne findSCB + mov DISCONNECTED_SCBH,SCB_NEXT ret - mvi INTSTAT,NO_MATCH /* not found - signal kernel */ - cmp RETURN_1,SCB_PAGEDIN je return - or SCSISIGO,ATNO /* assert ATNO */ - cmp ARG_1,SCB_LIST_NULL jne find_abort_tag - mvi MSG_ABORT call mk_mesg - jmp ITloop -find_abort_tag: - mvi MSG_ABORT_TAG call mk_mesg - jmp ITloop +set_stcnt_from_hcnt: + mov STCNT0, HCNT0 + mov STCNT1, HCNT1 + mov STCNT2, HCNT2 ret + +bcopy_7: + mov DINDIR, SINDIR + mov DINDIR, SINDIR +bcopy_5: + mov DINDIR, SINDIR +bcopy_4: + mov DINDIR, SINDIR +bcopy_3: + mov DINDIR, SINDIR + mov DINDIR, SINDIR + mov DINDIR, SINDIR ret + +dma_scb: + /* + * SCB index is in SINDEX. Determine the physical address in + * the host where this SCB is located and load HADDR with it. + */ + shr DINDEX, SINDEX, 3 + shl A, SINDEX, 5 + add HADDR0, A, HSCB_ADDR0 + mov A, DINDEX + adc HADDR1, A, HSCB_ADDR1 + clr A + adc HADDR2, A, HSCB_ADDR2 + adc HADDR3, A, HSCB_ADDR3 + /* Setup Count */ + mvi HCNT0, 28 + clr HCNT1 + clr HCNT2 + mov DFCNTRL, DMAPARAMS + test DMAPARAMS, DIRECTION jnz dma_scb_fromhost + /* Fill it with the SCB data */ + call copy_scb_tofifo + mvi DFCNTRL, 0xa /* HDMAEN | FIFOFLUSH */ +dma_scb_fromhost: + call dma_finish + /* If we were putting the SCB, we are done */ + test DMAPARAMS, DIRECTION jz return + mvi SCBARRAY call dfdat_in_7 + call dfdat_in_7_continued + call dfdat_in_7_continued + jmp dfdat_in_7_continued +dfdat_in_7: + mov DINDEX,SINDEX +dfdat_in_7_continued: + mov DINDIR,DFDAT + mov DINDIR,DFDAT + mov DINDIR,DFDAT + mov DINDIR,DFDAT + mov DINDIR,DFDAT + mov DINDIR,DFDAT + mov DINDIR,DFDAT ret + +copy_scb_tofifo: + mvi SCBARRAY call dfdat_out_7 + call dfdat_out_7 + call dfdat_out_7 +dfdat_out_7: + mov DFDAT,SINDIR + mov DFDAT,SINDIR + mov DFDAT,SINDIR + mov DFDAT,SINDIR + mov DFDAT,SINDIR + mov DFDAT,SINDIR + mov DFDAT,SINDIR ret /* - * Make a working copy of the scatter-gather parameters from the SCB. + * Wait for DMA from host memory to data FIFO to complete, then disable + * DMA and wait for it to acknowledge that it's off. */ -sg_scb2ram: - mov HADDR0, SCB_DATAPTR0 - mov HADDR1, SCB_DATAPTR1 - mov HADDR2, SCB_DATAPTR2 - mov HADDR3, SCB_DATAPTR3 - mov HCNT0, SCB_DATACNT0 - mov HCNT1, SCB_DATACNT1 - mov HCNT2, SCB_DATACNT2 - - mov STCNT0, HCNT0 - mov STCNT1, HCNT1 - mov STCNT2, HCNT2 +dma_finish: + test DFSTATUS,HDONE jz dma_finish + /* Turn off DMA preserving WIDEODD */ + and DFCNTRL,WIDEODD +dma_finish2: + test DFCNTRL,HDMAENACK jnz dma_finish2 + ret - mov SG_COUNT,SCB_SGCOUNT +index_untagged_scb: + shr SINDEX, SCB_TCL, 4 + and SINDEX, 0x03 /* Bottom two bits of tid */ + add SINDEX, SCB_ACTIVE0 + shr A, SCB_TCL, 6 /* Target ID divided by 4 */ + test SCB_TCL, SELBUSB jz index_untagged_scb2 + or A, 2 /* Add 2 positions */ +index_untagged_scb2: + mov SCBPTR, A /* + * Select the SCB with this + * target's information. + */ + mov DINDEX, SINDEX ret - mov SG_NEXT0, SCB_SGPTR0 - mov SG_NEXT1, SCB_SGPTR1 - mov SG_NEXT2, SCB_SGPTR2 - mov SG_NEXT3, SCB_SGPTR3 ret +get_free_or_disc_scb: + mov SINDEX, ALLZEROS + cmp FREE_SCBH, SCB_LIST_NULL jne get_free_scb + cmp DISCONNECTED_SCBH, SCB_LIST_NULL je return_error + mov SCBPTR, DISCONNECTED_SCBH /* - * Copying RAM values back to SCB, for Save Data Pointers message, but - * only if we've actually been into a data phase to change them. This - * protects against bogus data in scratch ram and the residual counts - * since they are only initialized when we go into data_in or data_out. + * If we are starting a new transaction, we have to ensure that + * there is at least one SCB left in case a reselection wins out. */ -sg_ram2scb: - test FLAGS, DPHASE jz return - mov SCB_SGCOUNT,SG_COUNT - - mov SCB_SGPTR0,SG_NEXT0 - mov SCB_SGPTR1,SG_NEXT1 - mov SCB_SGPTR2,SG_NEXT2 - mov SCB_SGPTR3,SG_NEXT3 - - mov SCB_DATAPTR0,SHADDR0 - mov SCB_DATAPTR1,SHADDR1 - mov SCB_DATAPTR2,SHADDR2 - mov SCB_DATAPTR3,SHADDR3 - + test FLAGS, RESELECTED jnz dequeue_disc_scb + cmp SCB_NEXT, SCB_LIST_NULL je return_error +dequeue_disc_scb: /* - * Use the residual number since STCNT is corrupted by any message transfer + * If we have a residual, then we are in the middle of some I/O + * and we have to send this SCB back up to the kernel so that the + * saved data pointers and residual information isn't lost. */ - mov SCB_DATACNT0,SCB_RESID_DCNT0 - mov SCB_DATACNT1,SCB_RESID_DCNT1 - mov SCB_DATACNT2,SCB_RESID_DCNT2 ret + test SCB_RESID_SGCNT,0xff jz unlink_disc_scb + mvi DMAPARAMS, 0x9 /* HDMAEN | FIFORESET*/ + mov SCB_TAG call dma_scb +unlink_disc_scb: + call rem_scb_from_disc_list + ret +get_free_scb: +/* + * If we are starting a new transaction, we have to ensure that + * there is at least one SCB left in case a reselection wins out. + */ + test FLAGS, RESELECTED jnz dequeue_free_scb + cmp SCB_NEXT, SCB_LIST_NULL jne dequeue_free_scb + cmp DISCONNECTED_SCBH, SCB_LIST_NULL je return_error +dequeue_free_scb: + mov SCBPTR, FREE_SCBH + mov FREE_SCBH, SCB_NEXT ret +return_error: + mvi SINDEX, SCB_LIST_NULL ret + +add_scb_to_free_list: + mov SCB_NEXT, FREE_SCBH + mov FREE_SCBH, SCBPTR ret +add_scb_to_disc_list: /* - * Add the array base TARG_SCRATCH to the target offset (the target address - * is in SCSIID), and return the result in SINDEX. The accumulator - * contains the 3->8 decoding of the target ID on return. + * Link this SCB into the DISCONNECTED list. This list holds the + * candidates for paging out an SCB if one is needed for a new command. + * Modifying the disconnected list is a critical(pause dissabled) section. */ -ndx_dtr: - shr A,SCSIID,4 - test SBLKCTL,SELBUSB jz ndx_dtr_2 - or A,0x08 /* Channel B entries add 8 */ -ndx_dtr_2: - add SINDEX,TARG_SCRATCH,A ret + mvi SCB_PREV, SCB_LIST_NULL + mov SCB_NEXT, DISCONNECTED_SCBH + mov DISCONNECTED_SCBH, SCBPTR + cmp SCB_NEXT,SCB_LIST_NULL je return + mov SCBPTR,SCB_NEXT + mov SCB_PREV,DISCONNECTED_SCBH + mov SCBPTR,DISCONNECTED_SCBH ret + |