📄 atari_ncr5380.c
字号:
/* * Function : void NCR5380_init (struct Scsi_Host *instance) * * Purpose : initializes *instance and corresponding 5380 chip. * * Inputs : instance - instantiation of the 5380 driver. * * Notes : I assume that the host, hostno, and id bits have been * set correctly. I don't care about the irq and other fields. * */static void __init NCR5380_init (struct Scsi_Host *instance, int flags){ int i; SETUP_HOSTDATA(instance); NCR5380_all_init(); hostdata->aborted = 0; hostdata->id_mask = 1 << instance->this_id; hostdata->id_higher_mask = 0; for (i = hostdata->id_mask; i <= 0x80; i <<= 1) if (i > hostdata->id_mask) hostdata->id_higher_mask |= i; for (i = 0; i < 8; ++i) hostdata->busy[i] = 0;#ifdef SUPPORT_TAGS init_tags();#endif#if defined (REAL_DMA) hostdata->dma_len = 0;#endif hostdata->targets_present = 0; hostdata->connected = NULL; hostdata->issue_queue = NULL; hostdata->disconnected_queue = NULL; hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT; if (!the_template) { the_template = instance->hostt; first_instance = instance; } #ifndef AUTOSENSE if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1)) printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n" " without AUTOSENSE option, contingent allegiance conditions may\n" " be incorrectly cleared.\n", HOSTNO);#endif /* def AUTOSENSE */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(TARGET_COMMAND_REG, 0); NCR5380_write(SELECT_ENABLE_REG, 0);}/* * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, * void (*done)(Scsi_Cmnd *)) * * Purpose : enqueues a SCSI command * * Inputs : cmd - SCSI command, done - function called on completion, with * a pointer to the command descriptor. * * Returns : 0 * * Side effects : * cmd is added to the per instance issue_queue, with minor * twiddling done to the host specific fields of cmd. If the * main coroutine is not running, it is restarted. * *//* Only make static if a wrapper function is used */#ifndef NCR5380_queue_commandstatic#endifint NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)){ SETUP_HOSTDATA(cmd->host); Scsi_Cmnd *tmp; int oldto; unsigned long flags; extern int update_timeout(Scsi_Cmnd * SCset, int timeout);#if (NDEBUG & NDEBUG_NO_WRITE) switch (cmd->cmnd[0]) { case WRITE_6: case WRITE_10: printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n", H_NO(cmd)); cmd->result = (DID_ERROR << 16); done(cmd); return 0; }#endif /* (NDEBUG & NDEBUG_NO_WRITE) */#ifdef NCR5380_STATS# if 0 if (!hostdata->connected && !hostdata->issue_queue && !hostdata->disconnected_queue) { hostdata->timebase = jiffies; }# endif# ifdef NCR5380_STAT_LIMIT if (cmd->request_bufflen > NCR5380_STAT_LIMIT)# endif switch (cmd->cmnd[0]) { case WRITE: case WRITE_6: case WRITE_10: hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase); hostdata->bytes_write[cmd->target] += cmd->request_bufflen; hostdata->pendingw++; break; case READ: case READ_6: case READ_10: hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase); hostdata->bytes_read[cmd->target] += cmd->request_bufflen; hostdata->pendingr++; break; }#endif /* * We use the host_scribble field as a pointer to the next command * in a queue */ NEXT(cmd) = NULL; cmd->scsi_done = done; cmd->result = 0; /* * 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 allegiance condition that exists and the * sense data is only guaranteed to be valid while the condition exists. */ save_flags(flags); cli(); /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA. * Otherwise a running NCR5380_main may steal the lock. * Lock before actually inserting due to fairness reasons explained in * atari_scsi.c. If we insert first, then it's impossible for this driver * to release the lock. * Stop timer for this command while waiting for the lock, or timeouts * may happen (and they really do), and it's no good if the command doesn't * appear in any of the queues. * ++roman: Just disabling the NCR interrupt isn't sufficient here, * because also a timer int can trigger an abort or reset, which would * alter queues and touch the lock. */ if (!IS_A_TT()) { oldto = update_timeout(cmd, 0); falcon_get_lock(); update_timeout(cmd, oldto); } if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { LIST(cmd, hostdata->issue_queue); NEXT(cmd) = hostdata->issue_queue; hostdata->issue_queue = cmd; } else { for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; NEXT(tmp); tmp = NEXT(tmp)) ; LIST(cmd, tmp); NEXT(tmp) = cmd; } restore_flags(flags); QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd), (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"); /* If queue_command() is called from an interrupt (real one or bottom * half), we let queue_main() do the job of taking care about main. If it * is already running, this is a no-op, else main will be queued. * * If we're not in an interrupt, we can call NCR5380_main() * unconditionally, because it cannot be already running. */ if (in_interrupt() || ((flags >> 8) & 7) >= 6) queue_main(); else NCR5380_main(); return 0;}/* * Function : NCR5380_main (void) * * Purpose : NCR5380_main is a coroutine 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 reentrancy and kernel stack overflow. */ static void NCR5380_main (void){ Scsi_Cmnd *tmp, *prev; struct Scsi_Host *instance = first_instance; struct NCR5380_hostdata *hostdata = HOSTDATA(instance); int done; unsigned long flags; /* * 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. * * ++roman: Just disabling the NCR interrupt isn't sufficient here, * because also a timer int can trigger an abort or reset, which can * alter queues and touch the Falcon lock. */ /* Tell int handlers main() is now already executing. Note that no races are possible here. If an int comes in before 'main_running' is set here, and queues/executes main via the task queue, it doesn't do any harm, just this instance of main won't find any work left to do. */ if (main_running) return; main_running = 1; save_flags(flags); do { cli(); /* Freeze request queues */ done = 1; if (!hostdata->connected) { MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO ); /* * Search through the issue_queue for a command destined * for a target that's not busy. */#if (NDEBUG & NDEBUG_LISTS) for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp)) ; /*printk("%p ", tmp);*/ if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/#endif for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) {#if (NDEBUG & NDEBUG_LISTS) if (prev != tmp) printk("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun);#endif /* When we find one, remove it from the issue queue. */ /* ++guenther: possible race with Falcon locking */ if (#ifdef SUPPORT_TAGS !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)#else !(hostdata->busy[tmp->target] & (1 << tmp->lun))#endif ) { cli(); /* ++guenther: just to be sure, this must be atomic */ if (prev) { REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); NEXT(prev) = NEXT(tmp); } else { REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp)); hostdata->issue_queue = NEXT(tmp); } NEXT(tmp) = NULL; falcon_dont_release++; /* reenable interrupts after finding one */ restore_flags(flags); /* * 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. */ MAIN_PRINTK("scsi%d: main(): command for target %d " "lun %d removed from issue_queue\n", HOSTNO, tmp->target, tmp->lun); /* * REQUEST SENSE commands are issued without tagged * queueing, even on SCSI-II devices because the * contingent allegiance condition exists for the * entire unit. */ /* ++roman: ...and the standard also requires that * REQUEST SENSE command are untagged. */ #ifdef SUPPORT_TAGS cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE );#endif if (!NCR5380_select(instance, tmp, (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) { falcon_dont_release--; /* release if target did not response! */ falcon_release_lock_if_possible( hostdata ); break; } else { cli(); LIST(tmp, hostdata->issue_queue); NEXT(tmp) = hostdata->issue_queue; hostdata->issue_queue = tmp;#ifdef SUPPORT_TAGS cmd_free_tag( tmp );#endif falcon_dont_release--; restore_flags(flags); MAIN_PRINTK("scsi%d: main(): select() failed, " "returned to issue_queue\n", HOSTNO); if (hostdata->connected) break; } } /* if target/lun/target queue is not busy */ } /* for issue_queue */ } /* if (!hostdata->connected) */ if (hostdata->connected #ifdef REAL_DMA && !hostdata->dma_len#endif ) { restore_flags(flags); MAIN_PRINTK("scsi%d: main: performing information transfer\n", HOSTNO); NCR5380_information_transfer(instance); MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO); done = 0; } } while (!done); /* Better allow ints _after_ 'main_running' has been cleared, else an interrupt could believe we'll pick up the work it left for us, but we won't see it anymore here... */ main_running = 0; restore_flags(flags);}#ifdef REAL_DMA/* * Function : void NCR5380_dma_complete (struct Scsi_Host *instance) * * Purpose : Called by interrupt handler when DMA finishes or a phase * mismatch occurs (which would finish the DMA transfer). * * Inputs : instance - this instance of the NCR5380. * */static void NCR5380_dma_complete( struct Scsi_Host *instance ){ SETUP_HOSTDATA(instance); int transfered, saved_data = 0, overrun = 0, cnt, toPIO; unsigned char **data, p; volatile int *count; if (!hostdata->connected) { printk(KERN_WARNING "scsi%d: received end of DMA interrupt with " "no connected cmd\n", HOSTNO); return; } if (atari_read_overruns) { p = hostdata->connected->SCp.phase; if (p & SR_IO) { udelay(10); if ((((NCR5380_read(BUS_AND_STATUS_REG)) & (BASR_PHASE_MATCH|BASR_ACK)) == (BASR_PHASE_MATCH|BASR_ACK))) { saved_data = NCR5380_read(INPUT_DATA_REG); overrun = 1; DMA_PRINTK("scsi%d: read overrun handled\n", HOSTNO); } } } DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG)); (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); transfered = hostdata->dma_len - NCR5380_dma_residual(instance); hostdata->dma_len = 0; data = (unsigned char **) &(hostdata->connected->SCp.ptr); count = &(hostdata->connected->SCp.this_residual); *data += transfered; *count -= transfered; if (atari_read_overruns) { if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) { cnt = toPIO = atari_read_overruns;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -