提交 11668bb6 编写于 作者: H Hannes Reinecke 提交者: James Bottomley

[SCSI] aic79xx: Sequencer update

Update sequencer code to Adaptec version 2.0.12-6.3.9.
Signed-off-by: NJames Bottomley <James.Bottomley@SteelEye.com>
上级 ba62cd2d
...@@ -75,8 +75,7 @@ struct scb_platform_data; ...@@ -75,8 +75,7 @@ struct scb_platform_data;
#define INITIATOR_WILDCARD (~0) #define INITIATOR_WILDCARD (~0)
#define SCB_LIST_NULL 0xFF00 #define SCB_LIST_NULL 0xFF00
#define SCB_LIST_NULL_LE (ahd_htole16(SCB_LIST_NULL)) #define SCB_LIST_NULL_LE (ahd_htole16(SCB_LIST_NULL))
#define QOUTFIFO_ENTRY_VALID 0x8000 #define QOUTFIFO_ENTRY_VALID 0x80
#define QOUTFIFO_ENTRY_VALID_LE (ahd_htole16(0x8000))
#define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL) #define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL)
#define SCSIID_TARGET(ahd, scsiid) \ #define SCSIID_TARGET(ahd, scsiid) \
...@@ -1053,6 +1052,13 @@ typedef uint8_t ahd_mode_state; ...@@ -1053,6 +1052,13 @@ typedef uint8_t ahd_mode_state;
typedef void ahd_callback_t (void *); typedef void ahd_callback_t (void *);
struct ahd_completion
{
uint16_t tag;
uint8_t sg_status;
uint8_t valid_tag;
};
struct ahd_softc { struct ahd_softc {
bus_space_tag_t tags[2]; bus_space_tag_t tags[2];
bus_space_handle_t bshs[2]; bus_space_handle_t bshs[2];
...@@ -1142,11 +1148,11 @@ struct ahd_softc { ...@@ -1142,11 +1148,11 @@ struct ahd_softc {
struct seeprom_config *seep_config; struct seeprom_config *seep_config;
/* Command Queues */ /* Command Queues */
struct ahd_completion *qoutfifo;
uint16_t qoutfifonext; uint16_t qoutfifonext;
uint16_t qoutfifonext_valid_tag; uint16_t qoutfifonext_valid_tag;
uint16_t qinfifonext; uint16_t qinfifonext;
uint16_t qinfifo[AHD_SCB_MAX]; uint16_t qinfifo[AHD_SCB_MAX];
uint16_t *qoutfifo;
/* /*
* Our qfreeze count. The sequencer compares * Our qfreeze count. The sequencer compares
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
* *
* $FreeBSD$ * $FreeBSD$
*/ */
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $" VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#76 $"
/* /*
* This file is processed by the aic7xxx_asm utility for use in assembling * This file is processed by the aic7xxx_asm utility for use in assembling
...@@ -65,13 +65,6 @@ VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $" ...@@ -65,13 +65,6 @@ VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $"
mvi MODE_PTR, MK_MODE(src, dst); \ mvi MODE_PTR, MK_MODE(src, dst); \
} }
#define TOGGLE_DFF_MODE \
if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
call toggle_dff_mode_work_around; \
} else { \
xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); \
}
#define RESTORE_MODE(mode) \ #define RESTORE_MODE(mode) \
if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \ if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
mov mode call set_mode_work_around; \ mov mode call set_mode_work_around; \
...@@ -1199,7 +1192,7 @@ register TARGPCISTAT { ...@@ -1199,7 +1192,7 @@ register TARGPCISTAT {
/* /*
* LQ Packet In * LQ Packet In
* The last LQ Packet received * The last LQ Packet recieved
*/ */
register LQIN { register LQIN {
address 0x020 address 0x020
...@@ -3542,10 +3535,34 @@ scratch_ram { ...@@ -3542,10 +3535,34 @@ scratch_ram {
COMPLETE_DMA_SCB_HEAD { COMPLETE_DMA_SCB_HEAD {
size 2 size 2
} }
/* Counting semaphore to prevent new select-outs */ /*
* tail of list of SCBs that have
* completed but need to be uploaded
* to the host prior to being completed.
*/
COMPLETE_DMA_SCB_TAIL {
size 2
}
/*
* head of list of SCBs that have
* been uploaded to the host, but cannot
* be completed until the QFREEZE is in
* full effect (i.e. no selections pending).
*/
COMPLETE_ON_QFREEZE_HEAD {
size 2
}
/*
* Counting semaphore to prevent new select-outs
* The queue is frozen so long as the sequencer
* and kernel freeze counts differ.
*/
QFREEZE_COUNT { QFREEZE_COUNT {
size 2 size 2
} }
KERNEL_QFREEZE_COUNT {
size 2
}
/* /*
* Mode to restore on legacy idle loop exit. * Mode to restore on legacy idle loop exit.
*/ */
...@@ -3624,6 +3641,17 @@ scratch_ram { ...@@ -3624,6 +3641,17 @@ scratch_ram {
QOUTFIFO_ENTRY_VALID_TAG { QOUTFIFO_ENTRY_VALID_TAG {
size 1 size 1
} }
/*
* Kernel and sequencer offsets into the queue of
* incoming target mode command descriptors. The
* queue is full when the KERNEL_TQINPOS == TQINPOS.
*/
KERNEL_TQINPOS {
size 1
}
TQINPOS {
size 1
}
/* /*
* Base address of our shared data with the kernel driver in host * Base address of our shared data with the kernel driver in host
* memory. This includes the qoutfifo and target mode * memory. This includes the qoutfifo and target mode
...@@ -3639,17 +3667,6 @@ scratch_ram { ...@@ -3639,17 +3667,6 @@ scratch_ram {
QOUTFIFO_NEXT_ADDR { QOUTFIFO_NEXT_ADDR {
size 4 size 4
} }
/*
* Kernel and sequencer offsets into the queue of
* incoming target mode command descriptors. The
* queue is full when the KERNEL_TQINPOS == TQINPOS.
*/
KERNEL_TQINPOS {
size 1
}
TQINPOS {
size 1
}
ARG_1 { ARG_1 {
size 1 size 1
mask SEND_MSG 0x80 mask SEND_MSG 0x80
...@@ -3951,6 +3968,7 @@ const SG_PREFETCH_ADDR_MASK download ...@@ -3951,6 +3968,7 @@ const SG_PREFETCH_ADDR_MASK download
const SG_SIZEOF download const SG_SIZEOF download
const PKT_OVERRUN_BUFOFFSET download const PKT_OVERRUN_BUFOFFSET download
const SCB_TRANSFER_SIZE download const SCB_TRANSFER_SIZE download
const CACHELINE_MASK download
/* /*
* BIOS SCB offsets * BIOS SCB offsets
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
* $FreeBSD$ * $FreeBSD$
*/ */
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#99 $" VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#119 $"
PATCH_ARG_LIST = "struct ahd_softc *ahd" PATCH_ARG_LIST = "struct ahd_softc *ahd"
PREFIX = "ahd_" PREFIX = "ahd_"
...@@ -68,13 +68,47 @@ no_error_set: ...@@ -68,13 +68,47 @@ no_error_set:
} }
SET_MODE(M_SCSI, M_SCSI) SET_MODE(M_SCSI, M_SCSI)
test SCSISEQ0, ENSELO|ENARBO jnz idle_loop_checkbus; 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; cmp WAITING_TID_HEAD[1], SCB_LIST_NULL je idle_loop_checkbus;
/* /*
* ENSELO is cleared by a SELDO, so we must test for SELDO * ENSELO is cleared by a SELDO, so we must test for SELDO
* one last time. * one last time.
*/ */
BEGIN_CRITICAL;
test SSTAT0, SELDO jnz select_out; test SSTAT0, SELDO jnz select_out;
END_CRITICAL; END_CRITICAL;
call start_selection; call start_selection;
...@@ -90,6 +124,13 @@ idle_loop_check_nonpackreq: ...@@ -90,6 +124,13 @@ idle_loop_check_nonpackreq:
test SSTAT2, NONPACKREQ jz . + 2; test SSTAT2, NONPACKREQ jz . + 2;
call unexpected_nonpkt_phase_find_ctxt; call unexpected_nonpkt_phase_find_ctxt;
if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) { if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
/*
* On Rev A. hardware, the busy LED is only
* turned on automaically during selections
* and re-selections. Make the LED status
* more useful by forcing it to be on so
* long as one of our data FIFOs is active.
*/
and A, FIFO0FREE|FIFO1FREE, DFFSTAT; and A, FIFO0FREE|FIFO1FREE, DFFSTAT;
cmp A, FIFO0FREE|FIFO1FREE jne . + 3; cmp A, FIFO0FREE|FIFO1FREE jne . + 3;
and SBLKCTL, ~DIAGLEDEN|DIAGLEDON; and SBLKCTL, ~DIAGLEDEN|DIAGLEDON;
...@@ -101,9 +142,9 @@ idle_loop_check_nonpackreq: ...@@ -101,9 +142,9 @@ idle_loop_check_nonpackreq:
call idle_loop_cchan; call idle_loop_cchan;
jmp idle_loop; jmp idle_loop;
BEGIN_CRITICAL;
idle_loop_gsfifo: idle_loop_gsfifo:
SET_MODE(M_SCSI, M_SCSI) SET_MODE(M_SCSI, M_SCSI)
BEGIN_CRITICAL;
idle_loop_gsfifo_in_scsi_mode: idle_loop_gsfifo_in_scsi_mode:
test LQISTAT2, LQIGSAVAIL jz return; test LQISTAT2, LQIGSAVAIL jz return;
/* /*
...@@ -152,11 +193,15 @@ END_CRITICAL; ...@@ -152,11 +193,15 @@ END_CRITICAL;
idle_loop_service_fifos: idle_loop_service_fifos:
SET_MODE(M_DFF0, M_DFF0) SET_MODE(M_DFF0, M_DFF0)
BEGIN_CRITICAL;
test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo; test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
call longjmp; call longjmp;
END_CRITICAL;
idle_loop_next_fifo: idle_loop_next_fifo:
SET_MODE(M_DFF1, M_DFF1) SET_MODE(M_DFF1, M_DFF1)
BEGIN_CRITICAL;
test LONGJMP_ADDR[1], INVALID_ADDR jz longjmp; test LONGJMP_ADDR[1], INVALID_ADDR jz longjmp;
END_CRITICAL;
return: return:
ret; ret;
...@@ -170,7 +215,6 @@ BEGIN_CRITICAL; ...@@ -170,7 +215,6 @@ BEGIN_CRITICAL;
test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle; test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog; test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
test CCSCBCTL, CCSCBDONE jz return; test CCSCBCTL, CCSCBDONE jz return;
END_CRITICAL;
/* FALLTHROUGH */ /* FALLTHROUGH */
scbdma_tohost_done: scbdma_tohost_done:
test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone; test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
...@@ -180,26 +224,18 @@ scbdma_tohost_done: ...@@ -180,26 +224,18 @@ scbdma_tohost_done:
* bad SCSI status (currently only for underruns), we * bad SCSI status (currently only for underruns), we
* queue the SCB for normal completion. Otherwise, we * queue the SCB for normal completion. Otherwise, we
* wait until any select-out activity has halted, and * wait until any select-out activity has halted, and
* then notify the host so that the transaction can be * then queue the completion.
* dealt with.
*/ */
test SCB_SCSI_STATUS, 0xff jnz scbdma_notify_host;
and CCSCBCTL, ~(CCARREN|CCSCBEN); and CCSCBCTL, ~(CCARREN|CCSCBEN);
bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2; 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 SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret; bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
scbdma_notify_host:
SET_MODE(M_SCSI, M_SCSI)
test SCSISEQ0, ENSELO jnz return;
test SSTAT0, (SELDO|SELINGO) jnz return;
SET_MODE(M_CCHAN, M_CCHAN)
/*
* Remove SCB and notify host.
*/
and CCSCBCTL, ~(CCARREN|CCSCBEN);
bmov COMPLETE_DMA_SCB_HEAD, SCB_NEXT_COMPLETE, 2;
SET_SEQINTCODE(BAD_SCB_STATUS)
ret;
fill_qoutfifo_dmadone: fill_qoutfifo_dmadone:
and CCSCBCTL, ~(CCARREN|CCSCBEN); and CCSCBCTL, ~(CCARREN|CCSCBEN);
call qoutfifo_updated; call qoutfifo_updated;
...@@ -208,6 +244,7 @@ fill_qoutfifo_dmadone: ...@@ -208,6 +244,7 @@ fill_qoutfifo_dmadone:
test QOFF_CTLSTA, SDSCB_ROLLOVR jz return; test QOFF_CTLSTA, SDSCB_ROLLOVR jz return;
bmov QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4; bmov QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4;
xor QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret; xor QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret;
END_CRITICAL;
qoutfifo_updated: qoutfifo_updated:
/* /*
...@@ -324,14 +361,15 @@ fill_qoutfifo: ...@@ -324,14 +361,15 @@ fill_qoutfifo:
* Keep track of the SCBs we are dmaing just * Keep track of the SCBs we are dmaing just
* in case the DMA fails or is aborted. * in case the DMA fails or is aborted.
*/ */
mov A, QOUTFIFO_ENTRY_VALID_TAG;
bmov COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2; bmov COMPLETE_SCB_DMAINPROG_HEAD, COMPLETE_SCB_HEAD, 2;
mvi CCSCBCTL, CCSCBRESET; mvi CCSCBCTL, CCSCBRESET;
bmov SCBHADDR, QOUTFIFO_NEXT_ADDR, 4; bmov SCBHADDR, QOUTFIFO_NEXT_ADDR, 4;
mov A, QOUTFIFO_NEXT_ADDR;
bmov SCBPTR, COMPLETE_SCB_HEAD, 2; bmov SCBPTR, COMPLETE_SCB_HEAD, 2;
fill_qoutfifo_loop: fill_qoutfifo_loop:
mov CCSCBRAM, SCBPTR; bmov CCSCBRAM, SCBPTR, 2;
or CCSCBRAM, A, SCBPTR[1]; mov CCSCBRAM, SCB_SGPTR[0];
mov CCSCBRAM, QOUTFIFO_ENTRY_VALID_TAG;
mov NONE, SDSCB_QOFF; mov NONE, SDSCB_QOFF;
inc INT_COALESCING_CMDCOUNT; inc INT_COALESCING_CMDCOUNT;
add CMDS_PENDING, -1; add CMDS_PENDING, -1;
...@@ -339,6 +377,18 @@ fill_qoutfifo_loop: ...@@ -339,6 +377,18 @@ fill_qoutfifo_loop:
cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done; cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done;
cmp CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done; cmp CCSCBADDR, CCSCBADDR_MAX je fill_qoutfifo_done;
test QOFF_CTLSTA, SDSCB_ROLLOVR jnz fill_qoutfifo_done; test QOFF_CTLSTA, SDSCB_ROLLOVR jnz fill_qoutfifo_done;
/*
* Don't cross an ADB or Cachline boundary when DMA'ing
* completion entries. In PCI mode, at least in 32/33
* configurations, the SCB DMA engine may lose its place
* in the data-stream should the target force a retry on
* something other than an 8byte aligned boundary. In
* PCI-X mode, we do this to avoid split transactions since
* many chipsets seem to be unable to format proper split
* completions to continue the data transfer.
*/
add SINDEX, A, CCSCBADDR;
test SINDEX, CACHELINE_MASK jz fill_qoutfifo_done;
bmov SCBPTR, SCB_NEXT_COMPLETE, 2; bmov SCBPTR, SCB_NEXT_COMPLETE, 2;
jmp fill_qoutfifo_loop; jmp fill_qoutfifo_loop;
fill_qoutfifo_done: fill_qoutfifo_done:
...@@ -354,7 +404,6 @@ dma_complete_scb: ...@@ -354,7 +404,6 @@ dma_complete_scb:
bmov SCBPTR, COMPLETE_DMA_SCB_HEAD, 2; bmov SCBPTR, COMPLETE_DMA_SCB_HEAD, 2;
bmov SCBHADDR, SCB_BUSADDR, 4; bmov SCBHADDR, SCB_BUSADDR, 4;
mvi CCARREN|CCSCBEN|CCSCBRESET jmp dma_scb; mvi CCARREN|CCSCBEN|CCSCBRESET jmp dma_scb;
END_CRITICAL;
/* /*
* Either post or fetch an SCB from host memory. The caller * Either post or fetch an SCB from host memory. The caller
...@@ -371,9 +420,19 @@ dma_scb: ...@@ -371,9 +420,19 @@ dma_scb:
mvi SCBHCNT, SCB_TRANSFER_SIZE; mvi SCBHCNT, SCB_TRANSFER_SIZE;
mov CCSCBCTL, SINDEX ret; mov CCSCBCTL, SINDEX ret;
BEGIN_CRITICAL;
setjmp: 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: setjmp_inline:
bmov LONGJMP_ADDR, STACK, 2; bmov LONGJMP_ADDR, STACK, 2;
longjmp: longjmp:
...@@ -392,11 +451,6 @@ set_mode_work_around: ...@@ -392,11 +451,6 @@ set_mode_work_around:
mvi SEQINTCTL, INTVEC1DSL; mvi SEQINTCTL, INTVEC1DSL;
mov MODE_PTR, SINDEX; mov MODE_PTR, SINDEX;
clr SEQINTCTL ret; clr SEQINTCTL ret;
toggle_dff_mode_work_around:
mvi SEQINTCTL, INTVEC1DSL;
xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1);
clr SEQINTCTL ret;
} }
...@@ -490,6 +544,21 @@ allocate_fifo1: ...@@ -490,6 +544,21 @@ allocate_fifo1:
SET_SRC_MODE M_SCSI; SET_SRC_MODE M_SCSI;
SET_DST_MODE M_SCSI; SET_DST_MODE M_SCSI;
select_in: select_in:
if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
/*
* On Rev A. hardware, the busy LED is only
* turned on automaically during selections
* and re-selections. Make the LED status
* more useful by forcing it to be on from
* the point of selection until our idle
* loop determines that neither of our FIFOs
* are busy. This handles the non-packetized
* case nicely as we will not return to the
* idle loop until the busfree at the end of
* each transaction.
*/
or SBLKCTL, DIAGLEDEN|DIAGLEDON;
}
if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) { if ((ahd->bugs & AHD_BUSFREEREV_BUG) != 0) {
/* /*
* Test to ensure that the bus has not * Test to ensure that the bus has not
...@@ -528,6 +597,21 @@ SET_SRC_MODE M_SCSI; ...@@ -528,6 +597,21 @@ SET_SRC_MODE M_SCSI;
SET_DST_MODE M_SCSI; SET_DST_MODE M_SCSI;
select_out: select_out:
BEGIN_CRITICAL; BEGIN_CRITICAL;
if ((ahd->bugs & AHD_FAINT_LED_BUG) != 0) {
/*
* On Rev A. hardware, the busy LED is only
* turned on automaically during selections
* and re-selections. Make the LED status
* more useful by forcing it to be on from
* the point of re-selection until our idle
* loop determines that neither of our FIFOs
* are busy. This handles the non-packetized
* case nicely as we will not return to the
* idle loop until the busfree at the end of
* each transaction.
*/
or SBLKCTL, DIAGLEDEN|DIAGLEDON;
}
/* Clear out all SCBs that have been successfully sent. */ /* Clear out all SCBs that have been successfully sent. */
if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) { if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
/* /*
...@@ -1000,15 +1084,9 @@ not_found_ITloop: ...@@ -1000,15 +1084,9 @@ not_found_ITloop:
/* /*
* We received a "command complete" message. Put the SCB on the complete * We received a "command complete" message. Put the SCB on the complete
* queue and trigger a completion interrupt via the idle loop. Before doing * queue and trigger a completion interrupt via the idle loop. Before doing
* so, check to see if there * so, check to see if there is a residual or the status byte is something
* is a residual or the status byte is something other than STATUS_GOOD (0). * other than STATUS_GOOD (0). In either of these conditions, we upload the
* In either of these conditions, we upload the SCB back to the host so it can * SCB back to the host so it can process this information.
* 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.
*/ */
mesgin_complete: mesgin_complete:
...@@ -1053,6 +1131,7 @@ complete_nomsg: ...@@ -1053,6 +1131,7 @@ complete_nomsg:
call queue_scb_completion; call queue_scb_completion;
jmp await_busfree; jmp await_busfree;
BEGIN_CRITICAL;
freeze_queue: freeze_queue:
/* Cancel any pending select-out. */ /* Cancel any pending select-out. */
test SSTAT0, SELDO|SELINGO jnz . + 2; test SSTAT0, SELDO|SELINGO jnz . + 2;
...@@ -1063,6 +1142,7 @@ freeze_queue: ...@@ -1063,6 +1142,7 @@ freeze_queue:
adc QFREEZE_COUNT[1], A; adc QFREEZE_COUNT[1], A;
or SEQ_FLAGS2, SELECTOUT_QFROZEN; or SEQ_FLAGS2, SELECTOUT_QFROZEN;
mov A, ACCUM_SAVE ret; mov A, ACCUM_SAVE ret;
END_CRITICAL;
/* /*
* Complete the current FIFO's SCB if data for this same * Complete the current FIFO's SCB if data for this same
...@@ -1085,8 +1165,10 @@ queue_scb_completion: ...@@ -1085,8 +1165,10 @@ queue_scb_completion:
test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */ test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb; test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
complete: complete:
BEGIN_CRITICAL;
bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2; bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret; bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
END_CRITICAL;
bad_status: bad_status:
cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb; cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
call freeze_queue; call freeze_queue;
...@@ -1097,9 +1179,18 @@ upload_scb: ...@@ -1097,9 +1179,18 @@ upload_scb:
* it on the host. * it on the host.
*/ */
bmov SCB_TAG, SCBPTR, 2; bmov SCB_TAG, SCBPTR, 2;
bmov SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2; BEGIN_CRITICAL;
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; 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;
/* /*
* Is it a disconnect message? Set a flag in the SCB to remind us * Is it a disconnect message? Set a flag in the SCB to remind us
...@@ -1146,8 +1237,18 @@ SET_DST_MODE M_DFF1; ...@@ -1146,8 +1237,18 @@ SET_DST_MODE M_DFF1;
await_busfree_clrchn: await_busfree_clrchn:
mvi DFFSXFRCTL, CLRCHN; mvi DFFSXFRCTL, CLRCHN;
await_busfree_not_m_dff: await_busfree_not_m_dff:
call clear_target_state; /* clear target specific flags */
mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT;
test SSTAT1,REQINIT|BUSFREE jz .; test SSTAT1,REQINIT|BUSFREE jz .;
/*
* We only set BUSFREE status once either a new
* phase has been detected or we are really
* BUSFREE. This allows the driver to know
* that we are active on the bus even though
* no identified transaction exists should a
* timeout occur while awaiting busfree.
*/
mvi LASTPHASE, P_BUSFREE;
test SSTAT1, BUSFREE jnz idle_loop; test SSTAT1, BUSFREE jnz idle_loop;
SET_SEQINTCODE(MISSED_BUSFREE) SET_SEQINTCODE(MISSED_BUSFREE)
...@@ -1202,11 +1303,6 @@ msgin_rdptrs_get_fifo: ...@@ -1202,11 +1303,6 @@ msgin_rdptrs_get_fifo:
call allocate_fifo; call allocate_fifo;
jmp mesgin_done; jmp mesgin_done;
clear_target_state:
mvi LASTPHASE, P_BUSFREE;
/* clear target specific flags */
mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;
phase_lock: phase_lock:
if ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0) { if ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0) {
/* /*
...@@ -1297,6 +1393,47 @@ service_fifo: ...@@ -1297,6 +1393,47 @@ service_fifo:
/* Are we actively fetching segments? */ /* Are we actively fetching segments? */
test CCSGCTL, CCSGENACK jnz return; 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 * We fetch a "cacheline aligned" and sized amount of data
* so we don't end up referencing a non-existant page. * so we don't end up referencing a non-existant page.
...@@ -1308,7 +1445,7 @@ service_fifo: ...@@ -1308,7 +1445,7 @@ service_fifo:
mvi SGHCNT, SG_PREFETCH_CNT; mvi SGHCNT, SG_PREFETCH_CNT;
if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) { if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) {
/* /*
* Need two instruction between "touches" of SGHADDR. * Need two instructions between "touches" of SGHADDR.
*/ */
nop; nop;
} }
...@@ -1658,7 +1795,7 @@ export seq_isr: ...@@ -1658,7 +1795,7 @@ export seq_isr:
* savepointer in the current FIFO. We do this so that * savepointer in the current FIFO. We do this so that
* a pending CTXTDONE or SAVEPTR is visible in the active * a pending CTXTDONE or SAVEPTR is visible in the active
* FIFO. This status is the only way we can detect if we * FIFO. This status is the only way we can detect if we
* have lost the race (e.g. host paused us) and our attepts * have lost the race (e.g. host paused us) and our attempts
* to disable the channel occurred after all REQs were * to disable the channel occurred after all REQs were
* already seen and acked (REQINIT never comes true). * already seen and acked (REQINIT never comes true).
*/ */
...@@ -1667,7 +1804,7 @@ export seq_isr: ...@@ -1667,7 +1804,7 @@ export seq_isr:
test DFCNTRL, DIRECTION jz interrupt_return; test DFCNTRL, DIRECTION jz interrupt_return;
and DFCNTRL, ~SCSIEN; and DFCNTRL, ~SCSIEN;
snapshot_wait_data_valid: snapshot_wait_data_valid:
test SEQINTSRC, (CTXTDONE|SAVEPTRS) jnz snapshot_data_valid; test SEQINTSRC, (CTXTDONE|SAVEPTRS) jnz interrupt_return;
test SSTAT1, REQINIT jz snapshot_wait_data_valid; test SSTAT1, REQINIT jz snapshot_wait_data_valid;
snapshot_data_valid: snapshot_data_valid:
or DFCNTRL, SCSIEN; or DFCNTRL, SCSIEN;
...@@ -1834,7 +1971,6 @@ pkt_saveptrs_check_status: ...@@ -1834,7 +1971,6 @@ pkt_saveptrs_check_status:
dec SCB_FIFO_USE_COUNT; dec SCB_FIFO_USE_COUNT;
test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle; test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
mvi DFFSXFRCTL, CLRCHN ret; mvi DFFSXFRCTL, CLRCHN ret;
END_CRITICAL;
/* /*
* LAST_SEG_DONE status has been seen in the current FIFO. * LAST_SEG_DONE status has been seen in the current FIFO.
...@@ -1843,7 +1979,6 @@ END_CRITICAL; ...@@ -1843,7 +1979,6 @@ END_CRITICAL;
* Check for overrun and see if we can complete this command. * Check for overrun and see if we can complete this command.
*/ */
pkt_last_seg_done: pkt_last_seg_done:
BEGIN_CRITICAL;
/* /*
* Mark transfer as completed. * Mark transfer as completed.
*/ */
......
此差异已折叠。
...@@ -839,7 +839,7 @@ ahd_sync_qoutfifo(struct ahd_softc *ahd, int op) ...@@ -839,7 +839,7 @@ ahd_sync_qoutfifo(struct ahd_softc *ahd, int op)
{ {
ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap, ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
/*offset*/0, /*offset*/0,
/*len*/AHD_SCB_MAX * sizeof(uint16_t), op); /*len*/AHD_SCB_MAX * sizeof(struct ahd_completion), op);
} }
static __inline void static __inline void
...@@ -871,8 +871,8 @@ ahd_check_cmdcmpltqueues(struct ahd_softc *ahd) ...@@ -871,8 +871,8 @@ ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap, ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
/*offset*/ahd->qoutfifonext * sizeof(*ahd->qoutfifo), /*offset*/ahd->qoutfifonext * sizeof(*ahd->qoutfifo),
/*len*/sizeof(*ahd->qoutfifo), BUS_DMASYNC_POSTREAD); /*len*/sizeof(*ahd->qoutfifo), BUS_DMASYNC_POSTREAD);
if ((ahd->qoutfifo[ahd->qoutfifonext] if (ahd->qoutfifo[ahd->qoutfifonext].valid_tag
& QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag) == ahd->qoutfifonext_valid_tag)
retval |= AHD_RUN_QOUTFIFO; retval |= AHD_RUN_QOUTFIFO;
#ifdef AHD_TARGET_MODE #ifdef AHD_TARGET_MODE
if ((ahd->flags & AHD_TARGETROLE) != 0 if ((ahd->flags & AHD_TARGETROLE) != 0
......
...@@ -1468,6 +1468,30 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev, ...@@ -1468,6 +1468,30 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev,
if ((tstate->auto_negotiate & mask) != 0) { if ((tstate->auto_negotiate & mask) != 0) {
scb->flags |= SCB_AUTO_NEGOTIATE; scb->flags |= SCB_AUTO_NEGOTIATE;
scb->hscb->control |= MK_MESSAGE; scb->hscb->control |= MK_MESSAGE;
} else if (cmd->cmnd[0] == INQUIRY
&& (tinfo->curr.offset != 0
|| tinfo->curr.width != MSG_EXT_WDTR_BUS_8_BIT
|| tinfo->curr.ppr_options != 0)
&& (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ)==0) {
/*
* The SCSI spec requires inquiry
* commands to complete without
* reporting unit attention conditions.
* Because of this, an inquiry command
* that occurs just after a device is
* reset will result in a data phase
* with mismatched negotiated rates.
* The core already forces a renegotiation
* for reset events that are visible to
* our controller or that we initiate,
* but a third party device reset or a
* hot-plug insertion can still cause this
* issue. Therefore, we force a re-negotiation
* for every inquiry command unless we
* are async.
*/
scb->flags |= SCB_NEGOTIATE;
scb->hscb->control |= MK_MESSAGE;
} }
if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) { if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
...@@ -2058,6 +2082,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) ...@@ -2058,6 +2082,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
int paused; int paused;
int wait; int wait;
int disconnected; int disconnected;
int found;
ahd_mode_state saved_modes; ahd_mode_state saved_modes;
unsigned long flags; unsigned long flags;
...@@ -2176,7 +2201,8 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) ...@@ -2176,7 +2201,8 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
last_phase = ahd_inb(ahd, LASTPHASE); last_phase = ahd_inb(ahd, LASTPHASE);
saved_scbptr = ahd_get_scbptr(ahd); saved_scbptr = ahd_get_scbptr(ahd);
active_scbptr = saved_scbptr; active_scbptr = saved_scbptr;
if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) { if (disconnected && ((last_phase != P_BUSFREE) ||
(ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0)) {
struct scb *bus_scb; struct scb *bus_scb;
bus_scb = ahd_lookup_scb(ahd, active_scbptr); bus_scb = ahd_lookup_scb(ahd, active_scbptr);
...@@ -2194,28 +2220,41 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) ...@@ -2194,28 +2220,41 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* bus or is in the disconnected state. * bus or is in the disconnected state.
*/ */
saved_scsiid = ahd_inb(ahd, SAVED_SCSIID); saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
if (last_phase != P_BUSFREE if (SCB_GET_TAG(pending_scb) == active_scbptr
&& (SCB_GET_TAG(pending_scb) == active_scbptr
|| (flag == SCB_DEVICE_RESET || (flag == SCB_DEVICE_RESET
&& SCSIID_TARGET(ahd, saved_scsiid) == scmd_id(cmd)))) { && SCSIID_TARGET(ahd, saved_scsiid) == scmd_id(cmd))) {
/* /*
* We're active on the bus, so assert ATN * We're active on the bus, so assert ATN
* and hope that the target responds. * and hope that the target responds.
*/ */
pending_scb = ahd_lookup_scb(ahd, active_scbptr); pending_scb = ahd_lookup_scb(ahd, active_scbptr);
pending_scb->flags |= SCB_RECOVERY_SCB|flag; pending_scb->flags |= SCB_RECOVERY_SCB|SCB_DEVICE_RESET;
ahd_outb(ahd, MSG_OUT, HOST_MSG); ahd_outb(ahd, MSG_OUT, HOST_MSG);
ahd_outb(ahd, SCSISIGO, last_phase|ATNO); ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n"); scmd_printk(KERN_INFO, cmd, "BDR message in message buffer\n");
wait = TRUE; wait = TRUE;
} else if (last_phase != P_BUSFREE
&& ahd_inb(ahd, SCSIPHASE) == 0) {
/*
* SCB is not identified, there
* is no pending REQ, and the sequencer
* has not seen a busfree. Looks like
* a stuck connection waiting to
* go busfree. Reset the bus.
*/
found = ahd_reset_channel(ahd, cmd->device->channel + 'A',
/*Initiate Reset*/TRUE);
printf("%s: Issued Channel %c Bus Reset. "
"%d SCBs aborted\n", ahd_name(ahd),
cmd->device->channel + 'A', found);
} else if (disconnected) { } else if (disconnected) {
/* /*
* Actually re-queue this SCB in an attempt * Actually re-queue this SCB in an attempt
* to select the device before it reconnects. * to select the device before it reconnects.
*/ */
pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; pending_scb->flags |= SCB_RECOVERY_SCB|flag;
ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb)); ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb));
pending_scb->hscb->cdb_len = 0; pending_scb->hscb->cdb_len = 0;
pending_scb->hscb->task_attribute = 0; pending_scb->hscb->task_attribute = 0;
...@@ -2296,16 +2335,17 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) ...@@ -2296,16 +2335,17 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
timer.expires = jiffies + (5 * HZ); timer.expires = jiffies + (5 * HZ);
timer.function = ahd_linux_sem_timeout; timer.function = ahd_linux_sem_timeout;
add_timer(&timer); add_timer(&timer);
printf("Recovery code sleeping\n"); printf("%s: Recovery code sleeping\n", ahd_name(ahd));
down(&ahd->platform_data->eh_sem); down(&ahd->platform_data->eh_sem);
printf("Recovery code awake\n"); printf("%s: Recovery code awake\n", ahd_name(ahd));
ret = del_timer_sync(&timer); ret = del_timer_sync(&timer);
if (ret == 0) { if (ret == 0) {
printf("Timer Expired\n"); printf("%s: Timer Expired (active %d)\n",
ahd_name(ahd), dev->active);
retval = FAILED; retval = FAILED;
} }
} }
ahd_unlock(ahd, &flags); ahd_unlock(ahd, &flags);
return (retval); return (retval);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册