?? ncr5380.c
字號:
/*
* Insert the cmd into the issue queue. Note that REQUEST SENSE
* commands are added to the head of the queue since any command will
* clear the contingent allegience condition that exists and the
* sense data is only guranteed to be valid while the condition exists.
*/
cli();
if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
hostdata->issue_queue = cmd;
} else {
for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble;
tmp = (Scsi_Cmnd *) tmp->host_scribble);
tmp->host_scribble = (unsigned char *) cmd;
}
#if (NDEBUG & NDEBUG_QUEUES)
printk("scsi%d : command added to %s of queue\n", instance->host_no,
(cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
#endif
/* Run the coroutine if it isn't allready running. */
run_main();
return 0;
}
/*
* Function : NCR5380_main (void)
*
* Purpose : NCR5380_main is a corroutine that runs as long as more work can
* be done on the NCR5380 host adapters in a system. Both
* NCR5380_queue_command() and NCR5380_intr() will try to start it
* in case it is not running.
*
* NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
* reenable them. This prevents rentrancy and kernel stack overflow.
*/
static void NCR5380_main (void) {
Scsi_Cmnd *tmp, *prev;
struct Scsi_Host *instance;
struct NCR5380_hostdata *hostdata;
int done;
/*
* We run (with interrupts disabled) until we're sure that none of
* the host adapters have anything that can be done, at which point
* we set main_running to 0 and exit.
*
* Interrupts are enabled before doing various other internal
* instructions, after we've decided that we need to run through
* the loop again.
*
* this should prevent any race conditions.
*/
do {
cli(); /* Freeze request queues */
done = 1;
for (instance = first_instance; instance && instance->hostt == the_template;
instance=instance->next) {
hostdata = (struct NCR5380_hostdata *) instance->hostdata;
cli();
if (!hostdata->connected) {
#if (NDEBUG & NDEBUG_MAIN)
printk("scsi%d : not connected\n", instance->host_no);
#endif
/*
* Search through the issue_queue for a command destined
* for a target that's not busy.
*/
for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
tmp->host_scribble)
/* When we find one, remove it from the issue queue. */
if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
if (prev)
prev->host_scribble = tmp->host_scribble;
else
hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
/* renable interrupts after finding one */
sti();
/*
* Attempt to establish an I_T_L nexus here.
* On success, instance->hostdata->connected is set.
* On failure, we must add the command back to the
* issue queue so we can keep trying.
*/
#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
printk("scsi%d : main() : command for target %d lun %d removed from issue_queue\n",
instance->host_no, tmp->target, tmp->lun);
#endif
/*
* REQUEST SENSE commands are issued without tagged
* queueing, even on SCSI-II devices because the
* contingent alligence condition exists for the
* entire unit.
*/
if (!NCR5380_select(instance, tmp,
(tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
TAG_NEXT)) {
break;
} else {
cli();
tmp->host_scribble = (unsigned char *)
hostdata->issue_queue;
hostdata->issue_queue = tmp;
sti();
#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
printk("scsi%d : main(): select() failed, returned to issue_queue\n",
instance->host_no);
#endif
}
} /* if target/lun is not busy */
} /* if (!hostdata->connected) */
if (hostdata->connected
#ifdef REAL_DMA
&& !hostdata->dmalen
#endif
#ifdef USLEEP
&& (!hostdata->time_expires || hostdata->time_expires >= jiffies)
#endif
) {
sti();
#if (NDEBUG & NDEBUG_MAIN)
printk("scsi%d : main() : performing information transfer\n",
instance->host_no);
#endif
NCR5380_information_transfer(instance);
#if (NDEBUG & NDEBUG_MAIN)
printk("scsi%d : main() : done set false\n", instance->host_no);
#endif
done = 0;
} else
break;
} /* for instance */
} while (!done);
main_running = 0;
}
/*
* Function : void NCR5380_intr (int irq)
*
* Purpose : handle interrupts, restablishing I_T_L or I_T_L_Q nexuses
* from the disconnected queue, and restarting NCR5380_main()
* as required.
*
* Inputs : int irq, irq that caused this interrupt.
*
*/
static void NCR5380_intr (int irq) {
NCR5380_local_declare();
struct Scsi_Host *instance;
int done;
unsigned char basr;
#if (NDEBUG & NDEBUG_INTR)
printk("scsi : NCR5380 irq %d triggered\n", irq);
#endif
do {
done = 1;
for (instance = first_instance; instance && (instance->hostt ==
the_template); instance = instance->next)
if (instance->irq == irq) {
/* Look for pending interrupts */
NCR5380_setup(instance);
basr = NCR5380_read(BUS_AND_STATUS_REG);
/* XXX dispatch to appropriate routine if found and done=0 */
if (basr & BASR_IRQ) {
#if (NDEBUG & NDEBUG_INTR)
NCR5380_print(instance);
#endif
if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
(SR_SEL | SR_IO)) {
done = 0;
sti();
#if (NDEBUG & NDEBUG_INTR)
printk("scsi%d : SEL interrupt\n", instance->host_no);
#endif
NCR5380_reselect(instance);
(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
} else if (basr &
BASR_PARITY_ERROR) {
#if (NDEBUG & NDEBUG_INTR)
printk("scsi%d : PARITY interrupt\n", instance->host_no);
#endif
(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
} else {
/*
* XXX the rest of the interrupt conditions should *only* occur during a
* DMA transfer, which I haven't gotten arround to fixing yet.
*/
#if defined(REAL_DMA)
/*
* We should only get PHASE MISMATCH and EOP interrupts
* if we have DMA enabled, so do a sanity check based on
* the current setting of the MODE register.
*/
if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr &
BASR_END_DMA_TRANSFER) ||
!(basr & BASR_PHASE_MATCH))) {
int transfered;
if (!hostdata->connected)
panic("scsi%d : recieved end of DMA interrupt with no connected cmd\n",
instance->hostno);
transfered = (hostdata->dmalen - NCR5380_dma_residual(instance));
hostdata->connected->SCp.this_residual -= transferred;
hostdata->connected->SCp.ptr += transferred;
hostdata->dmalen = 0;
(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
while (NCR5380_read(BUS_AND_STATUS_REG) &
BASR_ACK));
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
}
#else
#if (NDEBUG & NDEBUG_INTR)
printk("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
#endif
(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
#endif
}
} /* if BASR_IRQ */
if (!done)
run_main();
} /* if (instance->irq == irq) */
} while (!done);
}
/*
* Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
* int tag);
*
* Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
* including ARBITRATION, SELECTION, and initial message out for
* IDENTIFY and queue messages.
*
* Inputs : instance - instantiation of the 5380 driver on which this
* target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
* new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
* the command that is presently connected.
*
* Returns : -1 if selection could not execute for some reason,
* 0 if selection succeeeded or failed because the target
* did not respond.
*
* Side effects :
* If bus busy, arbitration failed, etc, NCR5380_select() will exit
* with registers as they should have been on entry - ie
* SELECT_ENABLE will be set appropriately, the NCR5380
* will cease to drive any SCSI bus signals.
*
* If successful : I_T_L or I_T_L_Q nexus will be established,
* instance->connected will be set to cmd.
* SELECT interrupt will be disabled.
*
* If failed (no target) : cmd->scsi_done() will be called, and the
* cmd->result host byte set to DID_BAD_TARGET.
*/
static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
int tag) {
NCR5380_local_declare();
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata*)
instance->hostdata;
unsigned char tmp[3], phase;
unsigned char *data;
int len;
unsigned long timeout;
NCR5380_setup(instance);
#if defined (NDEBUG) && (NDEBUG & NDEBUG_ARBITRATION)
NCR5380_print(instance);
printk("scsi%d : starting arbitration, id = %d\n", instance->host_no,
instance->this_id);
#endif
/*
* Set the phase bits to 0, otherwise the NCR5380 won't drive the
* data bus during SELECTION.
*/
NCR5380_write(TARGET_COMMAND_REG, 0);
/* Start arbitration */
NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
NCR5380_write(MODE_REG, MR_ARBITRATE);
/* Wait for arbitration logic to complete */
while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS));
#if (NDEBUG & NDEBUG_ARBITRATION)
printk("scsi%d : arbitration complete\n", instance->host_no);
/* Avoid GCC 2.4.5 asm needs to many reloads error */
__asm__("nop");
#endif
/*
* The arbitration delay is 2.2us, but this is a minimum and there is
* no maximum so we can safely sleep for ceil(2.2) usecs to accomodate
* the integral nature of udelay().
*
*/
udelay(3);
/* Check for lost arbitration */
if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
(NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
NCR5380_write(MODE_REG, MR_BASE);
#if (NDEBUG & NDEBUG_ARBITRATION)
printk("scsi%d : lost arbitration, deasserting MR_ARBITRATE\n",
instance->host_no);
#endif
return -1;
}
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) {
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
#if (NDEBUG & NDEBUG_ARBITRATION)
printk("scsi%d : lost arbitration, deasserting ICR_ASSERT_SEL\n",
instance->host_no);
#endif
return -1;
}
/*
* Again, bus clear + bus settle time is 1.2us, however, this is
* a minimum so we'll udelay ceil(1.2)
*/
udelay(2);
#if (NDEBUG & NDEBUG_ARBITRATION)
printk("scsi%d : won arbitration\n", instance->host_no);
#endif
/*
* Now that we have won arbitration, start Selection process, asserting
* the host and target ID's on the SCSI bus.
*/
NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
/*
* Raise ATN while SEL is true before BSY goes false from arbitration,
* since this is the only way to gurantee that we'll get a MESSAGE OUT
* phase immediately after selection.
*/
NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
NCR5380_write(MODE_REG, MR_BASE);
/*
* Reselect interrupts must be turned off prior to the dropping of BSY,
* otherwise we will trigger an interrupt.
*/
NCR5380_write(SELECT_ENABLE_REG, 0);
/* Reset BSY */
NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
ICR_ASSERT_ATN | ICR_ASSERT_SEL));
/*
* Something wierd happens when we cease to drive BSY - looks
* like the board/chip is letting us do another read before the
* appropriate propogation delay has expired, and we're confusing
* a BSY signal from ourselves as the target's response to SELECTION.
*
* A small delay (the 'C++' frontend breaks the pipeline with an
* unecessary jump, making it work on my 386-33/Trantor T128, the
* tighter 'C' code breaks and requires this) solves the problem -
* the 1 us delay is arbitrary, and only used because this delay will
* be the same on other platforms and since it works here, it should
* work there.
*/
udelay(1);
#if (NDEBUG & NDEBUG_SELECTION)
printk("scsi%d : selecting target %d\n", instance->host_no, cmd->target);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -