esp.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,423 行 · 第 1/5 页
C
2,423 行
}}static void esp_release_dmabufs(struct esp *esp, struct scsi_cmnd *sp){ if (sp->use_sg) { sbus_unmap_sg(esp->sdev, sp->buffer, sp->use_sg, sp->sc_data_direction); } else if (sp->request_bufflen) { sbus_unmap_single(esp->sdev, sp->SCp.have_data_in, sp->request_bufflen, sp->sc_data_direction); }}static void esp_restore_pointers(struct esp *esp, struct scsi_cmnd *sp){ struct esp_pointers *ep = &esp->data_pointers[sp->device->id]; sp->SCp.ptr = ep->saved_ptr; sp->SCp.buffer = ep->saved_buffer; sp->SCp.this_residual = ep->saved_this_residual; sp->SCp.buffers_residual = ep->saved_buffers_residual;}static void esp_save_pointers(struct esp *esp, struct scsi_cmnd *sp){ struct esp_pointers *ep = &esp->data_pointers[sp->device->id]; 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, struct 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){ struct scsi_cmnd *SCptr; struct scsi_device *SDptr; struct esp_device *esp_dev; 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; esp_dev = SDptr->hostdata; lun = SCptr->device->lun; target = SCptr->device->id; 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 (esp_dev->sync) { /* this targets sync is known */#ifndef __sparc_v9__do_sync_known:#endif if (esp_dev->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)) || !(esp_dev->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->device->lun)); if (!SDptr->borken && !esp_dev->disconnect) esp_dev->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... */ esp_dev->sync_max_offset = 0; esp_dev->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)) esp_dev->disconnect = 1; else esp_dev->disconnect = 0; esp_dev->sync_max_offset = 0; esp_dev->sync_min_period = 0; esp_dev->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 && !esp_dev->wide) { if (!SDptr->borken && SDptr->type != TYPE_ROM && SDptr->removable == 0) { build_wide_nego_msg(esp, 16); esp_dev->wide = 1; esp->wnip = 1; goto after_nego_msg_built; } else { esp_dev->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); } esp_dev->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 indefinitely * 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->device->id, SCptr->device->lun)); esp_dev->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 != esp_dev->sync_max_offset || esp->prev_stp != esp_dev->sync_min_period || (esp->erev > esp100a && esp->prev_cfg3 != esp->config3[target])) { esp->prev_soff = esp_dev->sync_max_offset; esp->prev_stp = esp_dev->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. */static int esp_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)){ struct esp *esp; /* 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->device->id, SCpnt->device->lun)); ESPDISC(("N<%02x,%02x>", SCpnt->device->id, SCpnt->device->lun)); esp = (struct esp *) SCpnt->device->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; /* 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); return 0;}/* Dump driver state. */static void esp_dump_cmd(struct scsi_cmnd *SCptr){ ESPLOG(("[tgt<%02x> lun<%02x> " "pphase<%s> cphase<%s>]", SCptr->device->id, SCptr->device->lun, phase_string(SCptr->SCp.sent_command), phase_string(SCptr->SCp.phase)));}static void esp_dump_state(struct esp *esp){ struct 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 = (struct scsi_cmnd *) SCptr->host_scribble; } ESPLOG(("\n"));}/* Abort a command. The host_lock is acquired by caller. */static int esp_abort(struct scsi_cmnd *SCptr){ struct esp *esp = (struct esp *) SCptr->device->host->hostdata; int don; ESPLOG(("esp%d: Aborting command\n", esp->esp_id)); esp_dump_state(esp);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?