📄 esp.c
字号:
static void esp_save_pointers(struct esp *esp, Scsi_Cmnd *sp){ struct esp_pointers *ep = &esp->data_pointers[sp->target]; ep->saved_ptr = sp->SCp.ptr; ep->saved_buffer = sp->SCp.buffer; ep->saved_this_residual = sp->SCp.this_residual; ep->saved_buffers_residual = sp->SCp.buffers_residual;}/* Some rules: * * 1) Never ever panic while something is live on the bus. * If there is to be any chance of syncing the disks this * rule is to be obeyed. * * 2) Any target that causes a foul condition will no longer * have synchronous transfers done to it, no questions * asked. * * 3) Keep register accesses to a minimum. Think about some * day when we have Xbus machines this is running on and * the ESP chip is on the other end of the machine on a * different board from the cpu where this is running. *//* Fire off a command. We assume the bus is free and that the only * case where we could see an interrupt is where we have disconnected * commands active and they are trying to reselect us. */static inline void esp_check_cmd(struct esp *esp, Scsi_Cmnd *sp){ switch (sp->cmd_len) { case 6: case 10: case 12: esp->esp_slowcmd = 0; break; default: esp->esp_slowcmd = 1; esp->esp_scmdleft = sp->cmd_len; esp->esp_scmdp = &sp->cmnd[0]; break; };}static inline void build_sync_nego_msg(struct esp *esp, int period, int offset){ esp->cur_msgout[0] = EXTENDED_MESSAGE; esp->cur_msgout[1] = 3; esp->cur_msgout[2] = EXTENDED_SDTR; esp->cur_msgout[3] = period; esp->cur_msgout[4] = offset; esp->msgout_len = 5;}/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */static inline void build_wide_nego_msg(struct esp *esp, int size){ esp->cur_msgout[0] = EXTENDED_MESSAGE; esp->cur_msgout[1] = 2; esp->cur_msgout[2] = EXTENDED_WDTR; switch (size) { case 32: esp->cur_msgout[3] = 2; break; case 16: esp->cur_msgout[3] = 1; break; case 8: default: esp->cur_msgout[3] = 0; break; }; esp->msgout_len = 4;}static void esp_exec_cmd(struct esp *esp){ Scsi_Cmnd *SCptr; Scsi_Device *SDptr; volatile u8 *cmdp = esp->esp_command; u8 the_esp_command; int lun, target; int i; /* Hold off if we have disconnected commands and * an IRQ is showing... */ if (esp->disconnected_SC && ESP_IRQ_P(esp->dregs)) return; /* Grab first member of the issue queue. */ SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC); /* Safe to panic here because current_SC is null. */ if (!SCptr) panic("esp: esp_exec_cmd and issue queue is NULL"); SDptr = SCptr->device; lun = SCptr->lun; target = SCptr->target; esp->snip = 0; esp->msgout_len = 0; /* Send it out whole, or piece by piece? The ESP * only knows how to automatically send out 6, 10, * and 12 byte commands. I used to think that the * Linux SCSI code would never throw anything other * than that to us, but then again there is the * SCSI generic driver which can send us anything. */ esp_check_cmd(esp, SCptr); /* If arbitration/selection is successful, the ESP will leave * ATN asserted, causing the target to go into message out * phase. The ESP will feed the target the identify and then * the target can only legally go to one of command, * datain/out, status, or message in phase, or stay in message * out phase (should we be trying to send a sync negotiation * message after the identify). It is not allowed to drop * BSY, but some buggy targets do and we check for this * condition in the selection complete code. Most of the time * we'll make the command bytes available to the ESP and it * will not interrupt us until it finishes command phase, we * cannot do this for command sizes the ESP does not * understand and in this case we'll get interrupted right * when the target goes into command phase. * * It is absolutely _illegal_ in the presence of SCSI-2 devices * to use the ESP select w/o ATN command. When SCSI-2 devices are * present on the bus we _must_ always go straight to message out * phase with an identify message for the target. Being that * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2 * selections should not confuse SCSI-1 we hope. */ if (SDptr->sync) { /* this targets sync is known */#ifndef __sparc_v9__do_sync_known:#endif if (SDptr->disconnect) *cmdp++ = IDENTIFY(1, lun); else *cmdp++ = IDENTIFY(0, lun); if (esp->esp_slowcmd) { the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); esp_advance_phase(SCptr, in_slct_stop); } else { the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); esp_advance_phase(SCptr, in_slct_norm); } } else if (!(esp->targets_present & (1<<target)) || !(SDptr->disconnect)) { /* After the bootup SCSI code sends both the * TEST_UNIT_READY and INQUIRY commands we want * to at least attempt allowing the device to * disconnect. */ ESPMISC(("esp: Selecting device for first time. target=%d " "lun=%d\n", target, SCptr->lun)); if (!SDptr->borken && !SDptr->disconnect) SDptr->disconnect = 1; *cmdp++ = IDENTIFY(0, lun); esp->prevmsgout = NOP; esp_advance_phase(SCptr, in_slct_norm); the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); /* Take no chances... */ SDptr->sync_max_offset = 0; SDptr->sync_min_period = 0; } else { /* Sorry, I have had way too many problems with * various CDROM devices on ESP. -DaveM */ int cdrom_hwbug_wkaround = 0;#ifndef __sparc_v9__ /* Never allow disconnects or synchronous transfers on * SparcStation1 and SparcStation1+. Allowing those * to be enabled seems to lockup the machine completely. */ if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { /* But we are nice and allow tapes and removable * disks (but not CDROMs) to disconnect. */ if(SDptr->type == TYPE_TAPE || (SDptr->type != TYPE_ROM && SDptr->removable)) SDptr->disconnect = 1; else SDptr->disconnect = 0; SDptr->sync_max_offset = 0; SDptr->sync_min_period = 0; SDptr->sync = 1; esp->snip = 0; goto do_sync_known; }#endif /* !(__sparc_v9__) */ /* We've talked to this guy before, * but never negotiated. Let's try, * need to attempt WIDE first, before * sync nego, as per SCSI 2 standard. */ if (esp->erev == fashme && !SDptr->wide) { if (!SDptr->borken && SDptr->type != TYPE_ROM && SDptr->removable == 0) { build_wide_nego_msg(esp, 16); SDptr->wide = 1; esp->wnip = 1; goto after_nego_msg_built; } else { SDptr->wide = 1; /* Fall through and try sync. */ } } if (!SDptr->borken) { if ((SDptr->type == TYPE_ROM)) { /* Nice try sucker... */ ESPMISC(("esp%d: Disabling sync for buggy " "CDROM.\n", esp->esp_id)); cdrom_hwbug_wkaround = 1; build_sync_nego_msg(esp, 0, 0); } else if (SDptr->removable != 0) { ESPMISC(("esp%d: Not negotiating sync/wide but " "allowing disconnect for removable media.\n", esp->esp_id)); build_sync_nego_msg(esp, 0, 0); } else { build_sync_nego_msg(esp, esp->sync_defp, 15); } } else { build_sync_nego_msg(esp, 0, 0); } SDptr->sync = 1; esp->snip = 1;after_nego_msg_built: /* A fix for broken SCSI1 targets, when they disconnect * they lock up the bus and confuse ESP. So disallow * disconnects for SCSI1 targets for now until we * find a better fix. * * Addendum: This is funny, I figured out what was going * on. The blotzed SCSI1 target would disconnect, * one of the other SCSI2 targets or both would be * disconnected as well. The SCSI1 target would * stay disconnected long enough that we start * up a command on one of the SCSI2 targets. As * the ESP is arbitrating for the bus the SCSI1 * target begins to arbitrate as well to reselect * the ESP. The SCSI1 target refuses to drop it's * ID bit on the data bus even though the ESP is * at ID 7 and is the obvious winner for any * arbitration. The ESP is a poor sport and refuses * to lose arbitration, it will continue indefinately * trying to arbitrate for the bus and can only be * stopped via a chip reset or SCSI bus reset. * Therefore _no_ disconnects for SCSI1 targets * thank you very much. ;-) */ if(((SDptr->scsi_level < 3) && (SDptr->type != TYPE_TAPE) && SDptr->removable == 0) || cdrom_hwbug_wkaround || SDptr->borken) { ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d " "lun %d\n", esp->esp_id, SCptr->target, SCptr->lun)); SDptr->disconnect = 0; *cmdp++ = IDENTIFY(0, lun); } else { *cmdp++ = IDENTIFY(1, lun); } /* ESP fifo is only so big... * Make this look like a slow command. */ esp->esp_slowcmd = 1; esp->esp_scmdleft = SCptr->cmd_len; esp->esp_scmdp = &SCptr->cmnd[0]; the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); esp_advance_phase(SCptr, in_slct_msg); } if (!esp->esp_slowcmd) for (i = 0; i < SCptr->cmd_len; i++) *cmdp++ = SCptr->cmnd[i]; /* HME sucks... */ if (esp->erev == fashme) sbus_writeb((target & 0xf) | (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT), esp->eregs + ESP_BUSID); else sbus_writeb(target & 7, esp->eregs + ESP_BUSID); if (esp->prev_soff != SDptr->sync_max_offset || esp->prev_stp != SDptr->sync_min_period || (esp->erev > esp100a && esp->prev_cfg3 != esp->config3[target])) { esp->prev_soff = SDptr->sync_max_offset; esp->prev_stp = SDptr->sync_min_period; sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); if (esp->erev > esp100a) { esp->prev_cfg3 = esp->config3[target]; sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); } } i = (cmdp - esp->esp_command); if (esp->erev == fashme) { esp_cmd(esp, ESP_CMD_FLUSH); /* Grrr! */ /* Set up the DMA and HME counters */ sbus_writeb(i, esp->eregs + ESP_TCLOW); sbus_writeb(0, esp->eregs + ESP_TCMED); sbus_writeb(0, esp->eregs + FAS_RLO); sbus_writeb(0, esp->eregs + FAS_RHI); esp_cmd(esp, the_esp_command); /* Talk about touchy hardware... */ esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | (DMA_SCSI_DISAB | DMA_ENABLE)) & ~(DMA_ST_WRITE)); sbus_writel(16, esp->dregs + DMA_COUNT); sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); } else { u32 tmp; /* Set up the DMA and ESP counters */ sbus_writeb(i, esp->eregs + ESP_TCLOW); sbus_writeb(0, esp->eregs + ESP_TCMED); tmp = sbus_readl(esp->dregs + DMA_CSR); tmp &= ~DMA_ST_WRITE; tmp |= DMA_ENABLE; sbus_writel(tmp, esp->dregs + DMA_CSR); if (esp->dma->revision == dvmaesc1) { if (i) /* Workaround ESC gate array SBUS rerun bug. */ sbus_writel(PAGE_SIZE, esp->dregs + DMA_COUNT); } sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); /* Tell ESP to "go". */ esp_cmd(esp, the_esp_command); }}/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){ struct esp *esp; unsigned long flags; /* Set up func ptr and initial driver cmd-phase. */ SCpnt->scsi_done = done; SCpnt->SCp.phase = not_issued; /* We use the scratch area. */ ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun)); ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun)); esp = (struct esp *) SCpnt->host->hostdata; esp_get_dmabufs(esp, SCpnt); esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */ SCpnt->SCp.Status = CHECK_CONDITION; SCpnt->SCp.Message = 0xff; SCpnt->SCp.sent_command = 0; spin_lock_irqsave(&esp->lock, flags); /* Place into our queue. */ if (SCpnt->cmnd[0] == REQUEST_SENSE) { ESPQUEUE(("RQSENSE\n")); prepend_SC(&esp->issue_SC, SCpnt); } else { ESPQUEUE(("\n")); append_SC(&esp->issue_SC, SCpnt); } /* Run it now if we can. */ if (!esp->current_SC && !esp->resetting_bus) esp_exec_cmd(esp); spin_unlock_irqrestore(&esp->lock, flags); return 0;}/* Only queuing supported in this ESP driver. */int esp_command(Scsi_Cmnd *SCpnt){ struct esp *esp = (struct esp *) SCpnt->host->hostdata; ESPLOG(("esp%d: esp_command() called...\n", esp->esp_id)); return -1;}/* Dump driver state. */static void esp_dump_cmd(Scsi_Cmnd *SCptr){ ESPLOG(("[tgt<%02x> lun<%02x> " "pphase<%s> cphase<%s>]", SCptr->target, SCptr->lun, phase_string(SCptr->SCp.sent_command), phase_string(SCptr->SCp.phase)));}static void esp_dump_state(struct esp *esp){ Scsi_Cmnd *SCptr = esp->current_SC;#ifdef DEBUG_ESP_CMDS int i;#endif ESPLOG(("esp%d: dumping state\n", esp->esp_id)); ESPLOG(("esp%d: dma -- cond_reg<%08x> addr<%08x>\n", esp->esp_id, sbus_readl(esp->dregs + DMA_CSR), sbus_readl(esp->dregs + DMA_ADDR))); ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n", esp->esp_id, esp->sreg, esp->seqreg, esp->ireg)); ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n", esp->esp_id, sbus_readb(esp->eregs + ESP_STATUS), sbus_readb(esp->eregs + ESP_SSTEP), sbus_readb(esp->eregs + ESP_INTRPT)));#ifdef DEBUG_ESP_CMDS printk("esp%d: last ESP cmds [", esp->esp_id); i = (esp->espcmdent - 1) & 31; printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); i = (i - 1) & 31; printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); i = (i - 1) & 31; printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); i = (i - 1) & 31; printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); printk("]\n");#endif /* (DEBUG_ESP_CMDS) */ if (SCptr) { ESPLOG(("esp%d: current command ", esp->esp_id)); esp_dump_cmd(SCptr); } ESPLOG(("\n")); SCptr = esp->disconnected_SC; ESPLOG(("esp%d: disconnected ", esp->esp_id)); while (SCptr) { esp_dump_cmd(SCptr); SCptr = (Scsi_Cmnd *) SCptr->host_scribble; } ESPLOG(("\n"));}/* Abort a command. */int esp_abort(Scsi_Cmnd *SCptr){ struct esp *esp = (struct esp *) SCptr->host->hostdata; unsigned long flags; int don; spin_lock_irqsave(&esp->lock, flags); ESPLOG(("esp%d: Aborting command\n", esp->esp_id)); esp_dump_state(esp); /* Wheee, if this is the current command on the bus, the * best we can do is assert ATN and wait for msgout phase. * This should even fix a hung SCSI bus when we lose state * in the driver and timeout because the eventual phase change * will cause the ESP to (eventually) give an interrupt. */ if (esp->current_SC == SCptr) { esp->cur_msgout[0] = ABORT; esp->msgout_len = 1; esp->msgout_ctr = 0; esp_cmd(esp, ESP_CMD_SATN);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -