?? ncr5380.c
字號:
* 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);#endif /* * The SCSI specification calls for a 250 ms timeout for the actual * selection. */ timeout = jiffies + 25; /* * XXX very interesting - we're seeing a bounce where the BSY we * asserted is being reflected / still asserted (propogation delay?) * and it's detecting as true. Sigh. */ while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY)); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); if (!(NCR5380_read(STATUS_REG) & SR_BSY)) { NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); cmd->result = DID_BAD_TARGET << 16; cmd->scsi_done(cmd); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);#if (NDEBUG & NDEBUG_SELECTION) printk("scsi%d : target did not respond within 250ms\n", instance->host_no);#endif return 0; } /* * Since we followed the SCSI spec, and raised ATN while SEL * was true but before BSY was false during selection, the information * transfer phase should be a MESSAGE OUT phase so that we can send the * IDENTIFY message. * * If SCSI-II tagged queing is enabled, we also send a SIMPLE_QUEUE_TAG * message (2 bytes) with a tag ID that we increment with every command * until it wraps back to 0. * * XXX - it turns out that there are some broken SCSI-II devices, * which claim to support tagged queing but fail when more than * some number of commands are issued at once. */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -