Commit 8aecd34f authored by Justin T. Gibbs's avatar Justin T. Gibbs

Aic79xx Driver Update

	Enable abort and bus device reset handlers for both legacy
	and packetized connections.
parent 5e332e62
......@@ -39,7 +39,7 @@
*
* $FreeBSD$
*/
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#59 $"
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.reg#60 $"
/*
* This file is processed by the aic7xxx_asm utility for use in assembling
......@@ -173,6 +173,23 @@ register SEQINTCODE {
STATUS_OVERRUN,
CFG4OVERRUN,
ENTERING_NONPACK,
TASKMGMT_FUNC_COMPLETE, /*
* Task management function
* request completed with
* an expected busfree.
*/
TASKMGMT_CMD_CMPLT_OKAY, /*
* A command with a non-zero
* task management function
* has completed via the normal
* command completion method
* for commands with a zero
* task management function.
* This happens when an attempt
* to abort a command loses
* the race for the command to
* complete normally.
*/
TRACEPOINT0,
TRACEPOINT1,
TRACEPOINT2,
......
......@@ -40,7 +40,7 @@
* $FreeBSD$
*/
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#77 $"
VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic79xx.seq#78 $"
PATCH_ARG_LIST = "struct ahd_softc *ahd"
PREFIX = "ahd_"
......@@ -107,6 +107,16 @@ idle_loop_gsfifo_in_scsi_mode:
good_status_IU_done:
bmov SCBPTR, GSFIFO, 2;
clr SCB_SCSI_STATUS;
/*
* If a command completed before an attempted task management
* function completed, notify the host after disabling any
* pending select-outs.
*/
test SCB_TASK_MANAGEMENT, 0xFF jz gsfifo_complete_normally;
test SSTAT0, SELDO|SELINGO jnz . + 2;
and SCSISEQ0, ~ENSELO;
SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY)
gsfifo_complete_normally:
or SCB_CONTROL, STATUS_RCVD;
/*
......@@ -471,7 +481,7 @@ select_in:
/*
* This exposes a window whereby a
* busfree just after a selection will
* be missed, but there is not other safe
* be missed, but there is no other safe
* way to enable busfree detection if
* the busfreerev function is broken.
*/
......@@ -597,7 +607,6 @@ select_out_inc_tid_q:
cmp WAITING_TID_HEAD[1], SCB_LIST_NULL jne . + 2;
mvi WAITING_TID_TAIL[1], SCB_LIST_NULL;
bmov SCBPTR, CURRSCB, 2;
END_CRITICAL;
mvi CLRSINT0, CLRSELDO;
test LQOSTAT2, LQOPHACHGOUTPKT jnz unexpected_nonpkt_phase;
test LQOSTAT1, LQOPHACHGINPKT jnz unexpected_nonpkt_phase;
......@@ -606,17 +615,23 @@ END_CRITICAL;
* If this is a packetized connection, return to our
* idle_loop and let our interrupt handler deal with
* any connection setup/teardown issues. The only
* exception is the case of MK_MESSAGE SCBs. In the
* A, the LQO manager transitions to LQOSTOP0 even if
* exceptions are the case of MK_MESSAGE and task management
* SCBs.
*/
if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) {
/*
* In the A, the LQO manager transitions to LQOSTOP0 even if
* we have selected out with ATN asserted and the target
* REQs in a non-packet phase.
*/
if ((ahd->bugs & AHD_LQO_ATNO_BUG) != 0) {
test SCB_CONTROL, MK_MESSAGE jz select_out_no_message;
test SCSISIGO, ATNO jnz select_out_non_packetized;
select_out_no_message:
}
test LQOSTAT2, LQOSTOP0 jnz idle_loop;
test LQOSTAT2, LQOSTOP0 jz select_out_non_packetized;
test SCB_TASK_MANAGEMENT, 0xFF jz idle_loop;
SET_SEQINTCODE(TASKMGMT_FUNC_COMPLETE)
jmp idle_loop;
select_out_non_packetized:
/* Non packetized request. */
......@@ -625,7 +640,7 @@ select_out_non_packetized:
/*
* This exposes a window whereby a
* busfree just after a selection will
* be missed, but there is not other safe
* be missed, but there is no other safe
* way to enable busfree detection if
* the busfreerev function is broken.
*/
......@@ -634,6 +649,8 @@ select_out_non_packetized:
}
mov SAVED_SCSIID, SCB_SCSIID;
mov SAVED_LUN, SCB_LUN;
mvi SEQ_FLAGS, NO_CDB_SENT;
END_CRITICAL;
or SXFRCTL0, SPIOEN;
/*
......@@ -642,7 +659,6 @@ select_out_non_packetized:
* asserted.
*/
mvi MSG_OUT, MSG_IDENTIFYFLAG;
mvi SEQ_FLAGS, NO_CDB_SENT;
/*
* Main loop for information transfer phases. Wait for the
......@@ -1608,7 +1624,12 @@ cfg4istat_setup_handler:
/*
* Status pkt is transferring to host.
* Wait in idle loop for transfer to complete.
* If a command completed before an attempted
* task management function completed, notify the host.
*/
test SCB_TASK_MANAGEMENT, 0xFF jz cfg4istat_no_taskmgmt_func;
SET_SEQINTCODE(TASKMGMT_CMD_CMPLT_OKAY)
cfg4istat_no_taskmgmt_func:
call pkt_handle_status;
or SEQINTCTL, IRET ret;
......
......@@ -37,7 +37,7 @@
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#150 $
* $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#151 $
*
* $FreeBSD$
*/
......@@ -438,11 +438,27 @@ ahd_flush_qoutfifo(struct ahd_softc *ahd)
scbid = next_scbid;
}
ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);
ahd_set_scbptr(ahd, saved_scbptr);
/*
* Flush the good status FIFO for compelted packetized commands.
*/
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
while ((ahd_inb(ahd, LQISTAT2) & LQIGSAVAIL) != 0) {
scbid = (ahd_inb(ahd, GSFIFO+1) << 8)
| ahd_inb(ahd, GSFIFO);
scb = ahd_lookup_scb(ahd, scbid);
if (scb == NULL) {
printf("%s: Warning - GSFIFO SCB %d invalid\n",
ahd_name(ahd), scbid);
continue;
}
ahd_complete_scb(ahd, scb);
}
/*
* Restore state.
*/
ahd_set_scbptr(ahd, saved_scbptr);
ahd_restore_modes(ahd, saved_modes);
ahd->flags |= AHD_UPDATE_PEND_CMDS;
}
......@@ -988,6 +1004,63 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
ahd_inb(ahd, SCB_CONTROL) & ~MK_MESSAGE);
break;
}
case TASKMGMT_FUNC_COMPLETE:
{
u_int scbid;
struct scb *scb;
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL) {
u_int lun;
u_int tag;
cam_status error;
lun = CAM_LUN_WILDCARD;
tag = SCB_LIST_NULL;
switch (scb->hscb->task_management) {
case SIU_TASKMGMT_ABORT_TASK:
tag = scb->hscb->tag;
case SIU_TASKMGMT_ABORT_TASK_SET:
case SIU_TASKMGMT_CLEAR_TASK_SET:
lun = scb->hscb->lun;
error = CAM_REQ_ABORTED;
break;
case SIU_TASKMGMT_LUN_RESET:
lun = scb->hscb->lun;
case SIU_TASKMGMT_TARGET_RESET:
error = CAM_BDR_SENT;
break;
default:
panic("Unexpected TaskMgmt Func\n");
break;
}
ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
'A', lun, tag, ROLE_INITIATOR, error);
}
break;
}
case TASKMGMT_CMD_CMPLT_OKAY:
{
u_int scbid;
struct scb *scb;
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL) {
/*
* Remove the second instance of this SCB from
* the QINFIFO if it is still there.
*/
ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
SCB_GET_CHANNEL(ahd, scb),
SCB_GET_LUN(scb), scb->hscb->tag,
ROLE_INITIATOR, /*status*/0,
SEARCH_REMOVE);
}
break;
}
case TRACEPOINT0:
case TRACEPOINT1:
case TRACEPOINT2:
......@@ -2410,8 +2483,9 @@ ahd_update_neg_request(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
* occurs the need to renegotiate is
* recorded persistently.
*/
tinfo->curr.period = AHD_PERIOD_UNKNOWN;
if ((ahd->features & AHD_WIDE) != 0)
tinfo->curr.width = AHD_WIDTH_UNKNOWN;
tinfo->curr.period = AHD_PERIOD_UNKNOWN;
tinfo->curr.offset = AHD_OFFSET_UNKNOWN;
}
if (tinfo->curr.period != tinfo->goal.period
......
/*
* Adaptec AIC79xx device driver for Linux.
*
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#105 $
* $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#106 $
*
* --------------------------------------------------------------------------
* Copyright (c) 1994-2000 Justin T. Gibbs.
......@@ -482,10 +482,8 @@ static void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd,
static void ahd_linux_filter_inquiry(struct ahd_softc *ahd,
struct ahd_devinfo *devinfo);
static void ahd_linux_dev_timed_unfreeze(u_long arg);
#if NO_YET
static void ahd_linux_sem_timeout(u_long arg);
static int ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
#endif
static void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd);
static void ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd);
static void ahd_linux_start_dv(struct ahd_softc *ahd);
......@@ -1211,31 +1209,13 @@ static int
ahd_linux_abort(Scsi_Cmnd *cmd)
{
struct ahd_softc *ahd;
u_long s;
#if NOTYET
struct ahd_cmd *acmd;
int found;
#endif
ahd = *(struct ahd_softc **)cmd->host->hostdata;
#if NOTYET
int error;
ahd = *(struct ahd_softc **)cmd->host->hostdata;
error = ahd_linux_queue_recovery_cmd(cmd, SCB_ABORT);
if (error != 0)
printf("aic79xx_abort returns 0x%x\n", error);
return (error);
#else
ahd_midlayer_entrypoint_lock(ahd, &s);
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
printf("%s: Abort called for cmd %p\n", ahd_name(ahd), cmd);
ahd_dump_card_state(ahd);
}
#endif
ahd_midlayer_entrypoint_unlock(ahd, &s);
return (FAILED);
#endif
}
/*
......@@ -1245,28 +1225,14 @@ static int
ahd_linux_dev_reset(Scsi_Cmnd *cmd)
{
struct ahd_softc *ahd;
#if NOTYET
struct ahd_cmd *acmd;
u_long s;
int found;
#endif
int error;
ahd = *(struct ahd_softc **)cmd->host->hostdata;
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
printf("%s: Dev reset called for cmd %p\n",
ahd_name(ahd), cmd);
#endif
#if NOTYET
int error;
error = ahd_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
if (error != 0)
printf("aic79xx_dev_reset returns 0x%x\n", error);
return (error);
#else
return (FAILED);
#endif
}
/*
......@@ -4536,8 +4502,11 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd,
printf("Copied %d bytes of sense data at %d:",
sense_size, sense_offset);
for (i = 0; i < sense_size; i++)
printf(" 0x%x", cmd->sense_buffer[i]);
for (i = 0; i < sense_size; i++) {
if ((i & 0xF) == 0)
printf("\n");
printf("0x%x ", cmd->sense_buffer[i]);
}
printf("\n");
}
#endif
......@@ -4941,7 +4910,6 @@ ahd_release_simq(struct ahd_softc *ahd)
ahd_schedule_runq(ahd);
}
#if NOT_YET
static void
ahd_linux_sem_timeout(u_long arg)
{
......@@ -4967,12 +4935,13 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
struct scb *pending_scb;
u_long s;
u_int saved_scbptr;
u_int active_scb_index;
u_int active_scbptr;
u_int last_phase;
int retval;
int paused;
int wait;
int disconnected;
ahd_mode_state saved_modes;
pending_scb = NULL;
paused = FALSE;
......@@ -5080,15 +5049,10 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
* didn't "just" miss an interrupt that would
* affect this cmd.
*/
ahd->flags |= AHD_ALL_INTERRUPTS;
do {
ahd_intr(ahd);
ahd_pause(ahd);
ahd_clear_critical_section(ahd);
} while (ahd_inb(ahd, INTSTAT) & INT_PEND);
ahd->flags &= ~AHD_ALL_INTERRUPTS;
ahd_pause_and_flushwork(ahd);
paused = TRUE;
if (bootverbose)
ahd_dump_card_state(ahd);
if ((pending_scb->flags & SCB_ACTIVE) == 0) {
......@@ -5116,6 +5080,23 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
disconnected = FALSE;
}
saved_modes = ahd_save_modes(ahd);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
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) {
struct scb *bus_scb;
bus_scb = ahd_lookup_scb(ahd, active_scbptr);
if (bus_scb == pending_scb)
disconnected = FALSE;
else if (flag != SCB_ABORT
&& ahd_inb(ahd, SAVED_SCSIID) == pending_scb->hscb->scsiid
&& ahd_inb(ahd, SAVED_LUN) == pending_scb->hscb->lun)
disconnected = FALSE;
}
/*
* At this point, pending_scb is the scb associated with the
* passed in command. That command is currently active on the
......@@ -5124,11 +5105,8 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
* send a BDR. Queue the appropriate message based on which of
* these states we are in.
*/
last_phase = ahd_inb(ahd, LASTPHASE);
saved_scbptr = ahd_inb(ahd, SCBPTR);
active_scb_index = ahd_inb(ahd, SCB_TAG);
if (last_phase != P_BUSFREE
&& (pending_scb->hscb->tag == active_scb_index
&& (pending_scb->hscb->tag == active_scbptr
|| (flag == SCB_DEVICE_RESET
&& SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)) == cmd->target))) {
......@@ -5136,7 +5114,7 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
* We're active on the bus, so assert ATN
* and hope that the target responds.
*/
pending_scb = ahd_lookup_scb(ahd, active_scb_index);
pending_scb = ahd_lookup_scb(ahd, active_scbptr);
pending_scb->flags |= SCB_RECOVERY_SCB|flag;
ahd_outb(ahd, MSG_OUT, HOST_MSG);
ahd_outb(ahd, SCSISIGO, last_phase|ATNO);
......@@ -5148,32 +5126,56 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
/*
* Actually re-queue this SCB in an attempt
* to select the device before it reconnects.
* In either case (selection or reselection),
* we will now issue the approprate message
* to the timed-out device.
*
* Set the MK_MESSAGE control bit indicating
* that we desire to send a message. We
* also set the disconnected flag since
* in the paging case there is no guarantee
* that our SCB control byte matches the
* version on the card. We don't want the
* sequencer to abort the command thinking
* an unsolicited reselection occurred.
*/
pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
pending_scb->flags |= SCB_RECOVERY_SCB|flag;
ahd_set_scbptr(ahd, pending_scb->hscb->tag);
pending_scb->hscb->cdb_len = 0;
pending_scb->hscb->task_attribute = 0;
switch (flag) {
case SCB_ABORT:
pending_scb->hscb->task_management =
SIU_TASKMGMT_ABORT_TASK;
break;
case SCB_DEVICE_RESET:
default:
pending_scb->hscb->task_management =
SIU_TASKMGMT_TARGET_RESET;
pending_scb->hscb->lun = 0;
memset(pending_scb->hscb->pkt_long_lun, 0, 8);
break;
}
if ((pending_scb->flags & SCB_PACKETIZED) != 0) {
/*
* Mark the SCB has having an outstanding
* task management function. Should the command
* complete normally before the task management
* function can be sent, the host will be notified
* to abort our requeued SCB.
*/
ahd_outb(ahd, SCB_TASK_MANAGEMENT,
pending_scb->hscb->task_management);
} else {
/*
* If non-packetized, set the MK_MESSAGE control
* bit indicating that we desire to send a message.
* We also set the disconnected flag since there is
* no guarantee that our SCB control byte matches
* the version on the card. We don't want the
* sequencer to abort the command thinking an
* unsolicited reselection occurred.
*/
pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
/*
* In the non-paging case, the sequencer will
* never re-reference the in-core SCB.
* To make sure we are notified during
* reslection, set the MK_MESSAGE flag in
* The sequencer will never re-reference the
* in-core SCB. To make sure we are notified
* during reslection, set the MK_MESSAGE flag in
* the card's copy of the SCB.
*/
ahd_outb(ahd, SCBPTR, pending_scb->hscb->tag);
ahd_outb(ahd, SCB_CONTROL,
ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE);
}
/*
* Clear out any entries in the QINFIFO first
......@@ -5183,12 +5185,10 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
ahd_search_qinfifo(ahd, cmd->target, cmd->channel + 'A',
cmd->lun, SCB_LIST_NULL, ROLE_INITIATOR,
CAM_REQUEUE_REQ, SEARCH_COMPLETE);
ahd_print_path(ahd, pending_scb);
printf("Queuing a recovery SCB\n");
ahd_qinfifo_requeue_tail(ahd, pending_scb);
ahd_outb(ahd, SCBPTR, saved_scbptr);
printf("%s:%d:%d:%d: Device is disconnected, re-queuing SCB\n",
ahd_name(ahd), cmd->channel, cmd->target, cmd->lun);
ahd_set_scbptr(ahd, saved_scbptr);
ahd_print_path(ahd, pending_scb);
printf("Device is disconnected, re-queuing SCB\n");
wait = TRUE;
} else {
printf("%s:%d:%d:%d: Unable to deliver message\n",
......@@ -5239,7 +5239,7 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
}
acmd = TAILQ_FIRST(&ahd->platform_data->completeq);
TAILQ_INIT(&ahd->platform_data->completeq);
ahd_midlayer_entry_unlock(ahd, &s);
ahd_midlayer_entrypoint_unlock(ahd, &s);
if (acmd != NULL) {
acmd = ahd_linux_run_complete_queue(ahd, acmd);
if (acmd != NULL) {
......@@ -5254,7 +5254,6 @@ ahd_linux_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
#endif
return (retval);
}
#endif
static void
ahd_linux_dev_timed_unfreeze(u_long arg)
......
......@@ -28,4 +28,12 @@ struct scsi_status_iu_header
(12 + (((siu)->flags & SIU_RSPVALID) \
? scsi_4btoul((siu)->pkt_failures_length) \
: 0))
#define SIU_TASKMGMT_NONE 0x00
#define SIU_TASKMGMT_ABORT_TASK 0x01
#define SIU_TASKMGMT_ABORT_TASK_SET 0x02
#define SIU_TASKMGMT_CLEAR_TASK_SET 0x04
#define SIU_TASKMGMT_LUN_RESET 0x08
#define SIU_TASKMGMT_TARGET_RESET 0x20
#define SIU_TASKMGMT_CLEAR_ACA 0x40
#endif /*_SCSI_SCSI_IU_H*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment