You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
[SCSI] aic79xx: Sequencer update
Update sequencer code to Adaptec version 2.0.12-6.3.9. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
committed by
James Bottomley
parent
ba62cd2d01
commit
11668bb673
@@ -75,8 +75,7 @@ struct scb_platform_data;
|
||||
#define INITIATOR_WILDCARD (~0)
|
||||
#define SCB_LIST_NULL 0xFF00
|
||||
#define SCB_LIST_NULL_LE (ahd_htole16(SCB_LIST_NULL))
|
||||
#define QOUTFIFO_ENTRY_VALID 0x8000
|
||||
#define QOUTFIFO_ENTRY_VALID_LE (ahd_htole16(0x8000))
|
||||
#define QOUTFIFO_ENTRY_VALID 0x80
|
||||
#define SCBID_IS_NULL(scbid) (((scbid) & 0xFF00 ) == SCB_LIST_NULL)
|
||||
|
||||
#define SCSIID_TARGET(ahd, scsiid) \
|
||||
@@ -1053,6 +1052,13 @@ typedef uint8_t ahd_mode_state;
|
||||
|
||||
typedef void ahd_callback_t (void *);
|
||||
|
||||
struct ahd_completion
|
||||
{
|
||||
uint16_t tag;
|
||||
uint8_t sg_status;
|
||||
uint8_t valid_tag;
|
||||
};
|
||||
|
||||
struct ahd_softc {
|
||||
bus_space_tag_t tags[2];
|
||||
bus_space_handle_t bshs[2];
|
||||
@@ -1142,11 +1148,11 @@ struct ahd_softc {
|
||||
struct seeprom_config *seep_config;
|
||||
|
||||
/* Command Queues */
|
||||
struct ahd_completion *qoutfifo;
|
||||
uint16_t qoutfifonext;
|
||||
uint16_t qoutfifonext_valid_tag;
|
||||
uint16_t qinfifonext;
|
||||
uint16_t qinfifo[AHD_SCB_MAX];
|
||||
uint16_t *qoutfifo;
|
||||
|
||||
/*
|
||||
* Our qfreeze count. The sequencer compares
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
*
|
||||
* $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
|
||||
@@ -65,13 +65,6 @@ VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#70 $"
|
||||
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) \
|
||||
if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { \
|
||||
mov mode call set_mode_work_around; \
|
||||
@@ -1199,7 +1192,7 @@ register TARGPCISTAT {
|
||||
|
||||
/*
|
||||
* LQ Packet In
|
||||
* The last LQ Packet received
|
||||
* The last LQ Packet recieved
|
||||
*/
|
||||
register LQIN {
|
||||
address 0x020
|
||||
@@ -3542,10 +3535,34 @@ scratch_ram {
|
||||
COMPLETE_DMA_SCB_HEAD {
|
||||
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 {
|
||||
size 2
|
||||
}
|
||||
KERNEL_QFREEZE_COUNT {
|
||||
size 2
|
||||
}
|
||||
/*
|
||||
* Mode to restore on legacy idle loop exit.
|
||||
*/
|
||||
@@ -3624,6 +3641,17 @@ scratch_ram {
|
||||
QOUTFIFO_ENTRY_VALID_TAG {
|
||||
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
|
||||
* memory. This includes the qoutfifo and target mode
|
||||
@@ -3639,17 +3667,6 @@ scratch_ram {
|
||||
QOUTFIFO_NEXT_ADDR {
|
||||
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 {
|
||||
size 1
|
||||
mask SEND_MSG 0x80
|
||||
@@ -3951,6 +3968,7 @@ const SG_PREFETCH_ADDR_MASK download
|
||||
const SG_SIZEOF download
|
||||
const PKT_OVERRUN_BUFOFFSET download
|
||||
const SCB_TRANSFER_SIZE download
|
||||
const CACHELINE_MASK download
|
||||
|
||||
/*
|
||||
* BIOS SCB offsets
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#99 $"
|
||||
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#119 $"
|
||||
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;
|
||||
@@ -90,6 +124,13 @@ idle_loop_check_nonpackreq:
|
||||
test SSTAT2, NONPACKREQ jz . + 2;
|
||||
call unexpected_nonpkt_phase_find_ctxt;
|
||||
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;
|
||||
cmp A, FIFO0FREE|FIFO1FREE jne . + 3;
|
||||
and SBLKCTL, ~DIAGLEDEN|DIAGLEDON;
|
||||
@@ -101,9 +142,9 @@ idle_loop_check_nonpackreq:
|
||||
call idle_loop_cchan;
|
||||
jmp idle_loop;
|
||||
|
||||
BEGIN_CRITICAL;
|
||||
idle_loop_gsfifo:
|
||||
SET_MODE(M_SCSI, M_SCSI)
|
||||
BEGIN_CRITICAL;
|
||||
idle_loop_gsfifo_in_scsi_mode:
|
||||
test LQISTAT2, LQIGSAVAIL jz return;
|
||||
/*
|
||||
@@ -152,11 +193,15 @@ END_CRITICAL;
|
||||
|
||||
idle_loop_service_fifos:
|
||||
SET_MODE(M_DFF0, M_DFF0)
|
||||
BEGIN_CRITICAL;
|
||||
test LONGJMP_ADDR[1], INVALID_ADDR jnz idle_loop_next_fifo;
|
||||
call longjmp;
|
||||
END_CRITICAL;
|
||||
idle_loop_next_fifo:
|
||||
SET_MODE(M_DFF1, M_DFF1)
|
||||
BEGIN_CRITICAL;
|
||||
test LONGJMP_ADDR[1], INVALID_ADDR jz longjmp;
|
||||
END_CRITICAL;
|
||||
return:
|
||||
ret;
|
||||
|
||||
@@ -170,7 +215,6 @@ BEGIN_CRITICAL;
|
||||
test CCSCBCTL, CCARREN|CCSCBEN jz scbdma_idle;
|
||||
test CCSCBCTL, CCSCBDIR jnz fetch_new_scb_inprog;
|
||||
test CCSCBCTL, CCSCBDONE jz return;
|
||||
END_CRITICAL;
|
||||
/* FALLTHROUGH */
|
||||
scbdma_tohost_done:
|
||||
test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
|
||||
@@ -180,26 +224,18 @@ scbdma_tohost_done:
|
||||
* bad SCSI status (currently only for underruns), we
|
||||
* queue the SCB for normal completion. Otherwise, we
|
||||
* wait until any select-out activity has halted, and
|
||||
* then notify the host so that the transaction can be
|
||||
* dealt with.
|
||||
* then queue the completion.
|
||||
*/
|
||||
test SCB_SCSI_STATUS, 0xff jnz scbdma_notify_host;
|
||||
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;
|
||||
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:
|
||||
and CCSCBCTL, ~(CCARREN|CCSCBEN);
|
||||
call qoutfifo_updated;
|
||||
@@ -208,6 +244,7 @@ fill_qoutfifo_dmadone:
|
||||
test QOFF_CTLSTA, SDSCB_ROLLOVR jz return;
|
||||
bmov QOUTFIFO_NEXT_ADDR, SHARED_DATA_ADDR, 4;
|
||||
xor QOUTFIFO_ENTRY_VALID_TAG, QOUTFIFO_ENTRY_VALID_TOGGLE ret;
|
||||
END_CRITICAL;
|
||||
|
||||
qoutfifo_updated:
|
||||
/*
|
||||
@@ -324,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;
|
||||
mov A, QOUTFIFO_NEXT_ADDR;
|
||||
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];
|
||||
mov CCSCBRAM, QOUTFIFO_ENTRY_VALID_TAG;
|
||||
mov NONE, SDSCB_QOFF;
|
||||
inc INT_COALESCING_CMDCOUNT;
|
||||
add CMDS_PENDING, -1;
|
||||
@@ -339,6 +377,18 @@ fill_qoutfifo_loop:
|
||||
cmp SCB_NEXT_COMPLETE[1], SCB_LIST_NULL je fill_qoutfifo_done;
|
||||
cmp CCSCBADDR, CCSCBADDR_MAX je 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;
|
||||
jmp fill_qoutfifo_loop;
|
||||
fill_qoutfifo_done:
|
||||
@@ -354,7 +404,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
|
||||
@@ -371,9 +420,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:
|
||||
@@ -392,11 +451,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -490,6 +544,21 @@ allocate_fifo1:
|
||||
SET_SRC_MODE M_SCSI;
|
||||
SET_DST_MODE M_SCSI;
|
||||
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) {
|
||||
/*
|
||||
* Test to ensure that the bus has not
|
||||
@@ -528,6 +597,21 @@ SET_SRC_MODE M_SCSI;
|
||||
SET_DST_MODE M_SCSI;
|
||||
select_out:
|
||||
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. */
|
||||
if ((ahd->bugs & AHD_SENT_SCB_UPDATE_BUG) != 0) {
|
||||
/*
|
||||
@@ -1000,15 +1084,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:
|
||||
|
||||
@@ -1053,6 +1131,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;
|
||||
@@ -1063,6 +1142,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
|
||||
@@ -1085,8 +1165,10 @@ queue_scb_completion:
|
||||
test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
|
||||
test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
|
||||
complete:
|
||||
BEGIN_CRITICAL;
|
||||
bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2;
|
||||
bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;
|
||||
END_CRITICAL;
|
||||
bad_status:
|
||||
cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb;
|
||||
call freeze_queue;
|
||||
@@ -1097,9 +1179,18 @@ upload_scb:
|
||||
* it on the host.
|
||||
*/
|
||||
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;
|
||||
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
|
||||
@@ -1146,8 +1237,18 @@ SET_DST_MODE M_DFF1;
|
||||
await_busfree_clrchn:
|
||||
mvi DFFSXFRCTL, CLRCHN;
|
||||
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 .;
|
||||
/*
|
||||
* 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;
|
||||
SET_SEQINTCODE(MISSED_BUSFREE)
|
||||
|
||||
@@ -1202,11 +1303,6 @@ msgin_rdptrs_get_fifo:
|
||||
call allocate_fifo;
|
||||
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:
|
||||
if ((ahd->bugs & AHD_EARLY_REQ_BUG) != 0) {
|
||||
/*
|
||||
@@ -1297,6 +1393,47 @@ service_fifo:
|
||||
/* Are we actively fetching segments? */
|
||||
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.
|
||||
@@ -1308,7 +1445,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;
|
||||
}
|
||||
@@ -1658,7 +1795,7 @@ export seq_isr:
|
||||
* savepointer in the current FIFO. We do this so that
|
||||
* a pending CTXTDONE or SAVEPTR is visible in the active
|
||||
* 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
|
||||
* already seen and acked (REQINIT never comes true).
|
||||
*/
|
||||
@@ -1667,7 +1804,7 @@ export seq_isr:
|
||||
test DFCNTRL, DIRECTION jz interrupt_return;
|
||||
and DFCNTRL, ~SCSIEN;
|
||||
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;
|
||||
snapshot_data_valid:
|
||||
or DFCNTRL, SCSIEN;
|
||||
@@ -1834,7 +1971,6 @@ pkt_saveptrs_check_status:
|
||||
dec SCB_FIFO_USE_COUNT;
|
||||
test SCB_CONTROL, STATUS_RCVD jnz pkt_complete_scb_if_fifos_idle;
|
||||
mvi DFFSXFRCTL, CLRCHN ret;
|
||||
END_CRITICAL;
|
||||
|
||||
/*
|
||||
* LAST_SEG_DONE status has been seen in the current FIFO.
|
||||
@@ -1843,7 +1979,6 @@ END_CRITICAL;
|
||||
* Check for overrun and see if we can complete this command.
|
||||
*/
|
||||
pkt_last_seg_done:
|
||||
BEGIN_CRITICAL;
|
||||
/*
|
||||
* Mark transfer as completed.
|
||||
*/
|
||||
|
||||
+382
-295
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
/*offset*/0,
|
||||
/*len*/AHD_SCB_MAX * sizeof(uint16_t), op);
|
||||
/*len*/AHD_SCB_MAX * sizeof(struct ahd_completion), op);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
@@ -871,8 +871,8 @@ ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
|
||||
ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
|
||||
/*offset*/ahd->qoutfifonext * sizeof(*ahd->qoutfifo),
|
||||
/*len*/sizeof(*ahd->qoutfifo), BUS_DMASYNC_POSTREAD);
|
||||
if ((ahd->qoutfifo[ahd->qoutfifonext]
|
||||
& QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag)
|
||||
if (ahd->qoutfifo[ahd->qoutfifonext].valid_tag
|
||||
== ahd->qoutfifonext_valid_tag)
|
||||
retval |= AHD_RUN_QOUTFIFO;
|
||||
#ifdef AHD_TARGET_MODE
|
||||
if ((ahd->flags & AHD_TARGETROLE) != 0
|
||||
|
||||
@@ -1468,6 +1468,30 @@ ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev,
|
||||
if ((tstate->auto_negotiate & mask) != 0) {
|
||||
scb->flags |= SCB_AUTO_NEGOTIATE;
|
||||
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) {
|
||||
@@ -2058,6 +2082,7 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
|
||||
int paused;
|
||||
int wait;
|
||||
int disconnected;
|
||||
int found;
|
||||
ahd_mode_state saved_modes;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -2176,7 +2201,8 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
|
||||
last_phase = ahd_inb(ahd, LASTPHASE);
|
||||
saved_scbptr = ahd_get_scbptr(ahd);
|
||||
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;
|
||||
|
||||
bus_scb = ahd_lookup_scb(ahd, active_scbptr);
|
||||
@@ -2194,28 +2220,41 @@ ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
|
||||
* bus or is in the disconnected state.
|
||||
*/
|
||||
saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
|
||||
if (last_phase != P_BUSFREE
|
||||
&& (SCB_GET_TAG(pending_scb) == active_scbptr
|
||||
if (SCB_GET_TAG(pending_scb) == active_scbptr
|
||||
|| (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
|
||||
* and hope that the target responds.
|
||||
*/
|
||||
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, 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;
|
||||
} 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) {
|
||||
|
||||
/*
|
||||
* Actually re-queue this SCB in an attempt
|
||||
* 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));
|
||||
pending_scb->hscb->cdb_len = 0;
|
||||
pending_scb->hscb->task_attribute = 0;
|
||||
@@ -2296,16 +2335,17 @@ done:
|
||||
timer.expires = jiffies + (5 * HZ);
|
||||
timer.function = ahd_linux_sem_timeout;
|
||||
add_timer(&timer);
|
||||
printf("Recovery code sleeping\n");
|
||||
printf("%s: Recovery code sleeping\n", ahd_name(ahd));
|
||||
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);
|
||||
if (ret == 0) {
|
||||
printf("Timer Expired\n");
|
||||
printf("%s: Timer Expired (active %d)\n",
|
||||
ahd_name(ahd), dev->active);
|
||||
retval = FAILED;
|
||||
}
|
||||
}
|
||||
ahd_unlock(ahd, &flags);
|
||||
ahd_unlock(ahd, &flags);
|
||||
return (retval);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user