diff options
Diffstat (limited to 'sys/dev/aic7xxx/aic79xx.seq')
-rw-r--r-- | sys/dev/aic7xxx/aic79xx.seq | 147 |
1 files changed, 115 insertions, 32 deletions
diff --git a/sys/dev/aic7xxx/aic79xx.seq b/sys/dev/aic7xxx/aic79xx.seq index 68c539b..7bd3b41 100644 --- a/sys/dev/aic7xxx/aic79xx.seq +++ b/sys/dev/aic7xxx/aic79xx.seq @@ -40,7 +40,7 @@ * $FreeBSD$ */ -VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#107 $" +VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#118 $" PATCH_ARG_LIST = "struct ahd_softc *ahd" PREFIX = "ahd_" @@ -68,13 +68,47 @@ no_error_set: } SET_MODE(M_SCSI, M_SCSI) test SCSISEQ0, ENSELO|ENARBO jnz idle_loop_checkbus; - test SEQ_FLAGS2, SELECTOUT_QFROZEN jnz idle_loop_checkbus; + test SEQ_FLAGS2, SELECTOUT_QFROZEN jz check_waiting_list; + /* + * If the kernel has caught up with us, thaw the queue. + */ + mov A, KERNEL_QFREEZE_COUNT; + cmp QFREEZE_COUNT, A jne check_frozen_completions; + mov A, KERNEL_QFREEZE_COUNT[1]; + cmp QFREEZE_COUNT[1], A jne check_frozen_completions; + and SEQ_FLAGS2, ~SELECTOUT_QFROZEN; + jmp check_waiting_list; +check_frozen_completions: + test SSTAT0, SELDO|SELINGO jnz idle_loop_checkbus; +BEGIN_CRITICAL; + /* + * If we have completions stalled waiting for the qfreeze + * to take effect, move them over to the complete_scb list + * now that no selections are pending. + */ + cmp COMPLETE_ON_QFREEZE_HEAD[1],SCB_LIST_NULL je idle_loop_checkbus; + /* + * Find the end of the qfreeze list. The first element has + * to be treated specially. + */ + bmov SCBPTR, COMPLETE_ON_QFREEZE_HEAD, 2; + cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je join_lists; + /* + * Now the normal loop. + */ + bmov SCBPTR, SCB_NEXT_COMPLETE, 2; + cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL jne . - 1; +join_lists: + bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2; + bmov COMPLETE_SCB_HEAD, COMPLETE_ON_QFREEZE_HEAD, 2; + mvi COMPLETE_ON_QFREEZE_HEAD[1], SCB_LIST_NULL; + jmp idle_loop_checkbus; +check_waiting_list: cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je idle_loop_checkbus; /* * ENSELO is cleared by a SELDO, so we must test for SELDO * one last time. */ -BEGIN_CRITICAL; test SSTAT0, SELDO jnz select_out; END_CRITICAL; call start_selection; @@ -192,14 +226,14 @@ scbdma_tohost_done: * wait until any select-out activity has halted, and * then queue the completion. */ - test SCB_SCSI_STATUS, 0xff jz scbdma_queue_completion; - SET_MODE(M_SCSI, M_SCSI) - test SCSISEQ0, ENSELO jnz return; - test SSTAT0, (SELDO|SELINGO) jnz return; - SET_MODE(M_CCHAN, M_CCHAN) -scbdma_queue_completion: and CCSCBCTL, ~(CCARREN|CCSCBEN); bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2; + cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL jne . + 2; + mvi COMPLETE_DMA_SCB_TAIL[1], SCB_LIST_NULL; + test SCB_SCSI_STATUS, 0xff jz scbdma_queue_completion; + bmov SCB_NEXT_COMPLETE, COMPLETE_ON_QFREEZE_HEAD, 2; + bmov COMPLETE_ON_QFREEZE_HEAD, SCBPTR, 2 ret; +scbdma_queue_completion: bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2; bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret; fill_qoutfifo_dmadone: @@ -327,14 +361,15 @@ fill_qoutfifo: * Keep track of the SCBs we are dmaing just * in case the DMA fails or is aborted. */ - mov A, QOUTFIFO_ENTRY_VALID_TAG; bmov COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2; mvi CCSCBCTL, CCSCBRESET; bmov SCBHADDR, QOUTFIFO_NEXT_ADDR, 4; bmov SCBPTR, COMPLETE_SCB_HEAD, 2; fill_qoutfifo_loop: - mov CCSCBRAM, SCBPTR; - or CCSCBRAM, A, SCBPTR[1]; + bmov CCSCBRAM, SCBPTR, 2; + mov CCSCBRAM, SCB_SGPTR[0]; + bmov CCSCBRAM, ALLZEROS, 4; + mov CCSCBRAM, QOUTFIFO_ENTRY_VALID_TAG; mov NONE, SDSCB_QOFF; inc INT_COALESCING_CMDCOUNT; add CMDS_PENDING, -1; @@ -357,7 +392,6 @@ dma_complete_scb: bmov SCBPTR, COMPLETE_DMA_SCB_HEAD, 2; bmov SCBHADDR, SCB_BUSADDR, 4; mvi CCARREN|CCSCBEN|CCSCBRESET jmp dma_scb; -END_CRITICAL; /* * Either post or fetch an SCB from host memory. The caller @@ -374,9 +408,19 @@ dma_scb: mvi SCBHCNT, SCB_TRANSFER_SIZE; mov CCSCBCTL, SINDEX ret; -BEGIN_CRITICAL; setjmp: - bmov LONGJMP_ADDR, STACK, 2 ret; + /* + * At least on the A, a return in the same + * instruction as the bmov results in a return + * to the caller, not to the new address at the + * top of the stack. Since we want the latter + * (we use setjmp to register a handler from an + * interrupt context but not invoke that handler + * until we return to our idle loop), use a + * separate ret instruction. + */ + bmov LONGJMP_ADDR, STACK, 2; + ret; setjmp_inline: bmov LONGJMP_ADDR, STACK, 2; longjmp: @@ -395,11 +439,6 @@ set_mode_work_around: mvi SEQINTCTL, INTVEC1DSL; mov MODE_PTR, SINDEX; clr SEQINTCTL ret; - -toggle_dff_mode_work_around: - mvi SEQINTCTL, INTVEC1DSL; - xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); - clr SEQINTCTL ret; } @@ -1033,15 +1072,9 @@ not_found_ITloop: /* * We received a "command complete" message. Put the SCB on the complete * queue and trigger a completion interrupt via the idle loop. Before doing - * so, check to see if there - * is a residual or the status byte is something other than STATUS_GOOD (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, requeue - * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting - * RETURN_1 to SEND_SENSE. + * so, check to see if there is a residual or the status byte is something + * other than STATUS_GOOD (0). In either of these conditions, we upload the + * SCB back to the host so it can process this information. */ mesgin_complete: @@ -1086,6 +1119,7 @@ complete_nomsg: call queue_scb_completion; jmp await_busfree; +BEGIN_CRITICAL; freeze_queue: /* Cancel any pending select-out. */ test SSTAT0, SELDO|SELINGO jnz . + 2; @@ -1096,6 +1130,7 @@ freeze_queue: adc QFREEZE_COUNT[1], A; or SEQ_FLAGS2, SELECTOUT_QFROZEN; mov A, ACCUM_SAVE ret; +END_CRITICAL; /* * Complete the current FIFO's SCB if data for this same @@ -1133,9 +1168,16 @@ upload_scb: */ bmov SCB_TAG, SCBPTR, 2; BEGIN_CRITICAL; - bmov SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2; + or SCB_SGPTR, SG_STATUS_VALID; + mvi SCB_NEXT_COMPLETE[1], SCB_LIST_NULL; + cmp COMPLETE_DMA_SCB_HEAD[1], SCB_LIST_NULL jne add_dma_scb_tail; bmov COMPLETE_DMA_SCB_HEAD, SCBPTR, 2; - or SCB_SGPTR, SG_STATUS_VALID ret; + bmov COMPLETE_DMA_SCB_TAIL, SCBPTR, 2 ret; +add_dma_scb_tail: + bmov REG0, SCBPTR, 2; + bmov SCBPTR, COMPLETE_DMA_SCB_TAIL, 2; + bmov SCB_NEXT_COMPLETE, REG0, 2; + bmov COMPLETE_DMA_SCB_TAIL, REG0, 2 ret; END_CRITICAL; /* @@ -1340,6 +1382,47 @@ service_fifo: test CCSGCTL, CCSGENACK jnz return; /* + * Should the other FIFO get the S/G cache first? If + * both FIFOs have been allocated since we last checked + * any FIFO, it is important that we service a FIFO + * that is not actively on the bus first. This guarantees + * that a FIFO will be freed to handle snapshot requests for + * any FIFO that is still on the bus. Chips with RTI do not + * perform snapshots, so don't bother with this test there. + */ + if ((ahd->features & AHD_RTI) == 0) { + /* + * If we're not still receiving SCSI data, + * it is safe to allocate the S/G cache to + * this FIFO. + */ + test DFCNTRL, SCSIEN jz idle_sgfetch_start; + + /* + * Switch to the other FIFO. Non-RTI chips + * also have the "set mode" bug, so we must + * disable interrupts during the switch. + */ + mvi SEQINTCTL, INTVEC1DSL; + xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); + + /* + * If the other FIFO needs loading, then it + * must not have claimed the S/G cache yet + * (SG_CACHE_AVAIL would have been cleared in + * the orginal FIFO mode and we test this above). + * Return to the idle loop so we can process the + * FIFO not currently on the bus first. + */ + test SG_STATE, LOADING_NEEDED jz idle_sgfetch_okay; + clr SEQINTCTL ret; +idle_sgfetch_okay: + xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); + clr SEQINTCTL; + } + +idle_sgfetch_start: + /* * We fetch a "cacheline aligned" and sized amount of data * so we don't end up referencing a non-existant page. * Cacheline aligned is in quotes because the kernel will @@ -1350,7 +1433,7 @@ service_fifo: mvi SGHCNT, SG_PREFETCH_CNT; if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) { /* - * Need two instruction between "touches" of SGHADDR. + * Need two instructions between "touches" of SGHADDR. */ nop; } |