📄 esp.c
字号:
(bp->val[0] == -1 && bp->val[1] == sc->sc_dev.dv_unit)) bp = sa->sa_ra.ra_bp; if (bp != NULL && strcmp(bp->name, "esp") == 0 && SAME_ESP(bp, sa)) sc->sc_bp = bp + 1; espdoattach(sc->sc_dev.dv_unit);}/* * `Final' attach of esp occurs once esp and dma chips have been found * and assigned virtual addresses. Set up the ESP SCSI data structures * and probe the SCSI bus. */static voidespdoattach(unit) int unit;{ register struct esp_softc *sc; register struct dma_softc *dc; register struct bootpath *bp; register struct targ *t; register int targ, u; /* make sure we have both */ if (espcd.cd_ndevs <= unit || dmacd.cd_ndevs <= unit || (sc = espcd.cd_devs[unit]) == NULL || (dc = dmacd.cd_devs[unit]) == NULL) return; sc->sc_dc = dc; sc->sc_dma = dc->dc_dma; sc->sc_hba.hba_driver = &esphbadriver; sc->sc_dma->dma_csr = 0; /* ??? */ espreset(sc, RESET_ESPCHIP); /* MAYBE THIS SHOULD BE MOVED TO scsi_subr.c? */ for (targ = 0; targ < 8; targ++) { if (targ == sc->sc_id) continue; sc->sc_probing = PROBE_TESTING; sc->sc_clearing = 1; (void)scsi_test_unit_ready(&sc->sc_hba, targ, 0); if (sc->sc_probing != PROBE_NO_TARGET) { sc->sc_probing = 0; sc->sc_clearing = 0; SCSI_FOUNDTARGET(&sc->sc_hba, targ); } } sc->sc_probing = 0; sc->sc_clearing = 0; /* * See if we booted from a unit on this target. We could * compare bp->name against the unit's name but there's no * real need since a target and unit uniquely specify a * scsi device. */ if ((bp = sc->sc_bp) != NULL && (u_int)(targ = bp->val[0]) < 8 && (u_int)(u = bp->val[1]) < 8 && (t = sc->sc_hba.hba_targets[targ]) != NULL && t->t_units[u] != NULL) bootdv = t->t_units[u]->u_dev;}/* * We are not allowed to touch the DMA "flush" and "drain" bits * while it is still thinking about a request (DMA_RP). */#define DMAWAIT(dma) while ((dma)->dma_csr & DMA_RP) DELAY(1)/* * Reset the DMA chip. */static voiddmareset(sc) struct esp_softc *sc;{ register volatile struct dmareg *dma = sc->sc_dma; DMAWAIT(dma); dma->dma_csr |= DMA_RESET; DELAY(200); dma->dma_csr &= ~DMA_RESET; /* ??? */ sc->sc_state = S_IDLE; sc->sc_dmaactive = 0; if (sc->sc_dc->dc_dmarev == DMAREV_2 && sc->sc_esptype != ESP100) dma->dma_csr |= DMA_TURBO; dma->dma_csr |= DMA_IE; /* enable interrupts */ DELAY(200);}/* * Reset the chip and/or SCSI bus (always resets DMA). */static voidespreset(sc, how) register struct esp_softc *sc; int how;{ register volatile struct espreg *esp = sc->sc_esp; dmareset(sc); if (how & RESET_ESPCHIP) { esp->esp_cmd = ESPCMD_RESET_CHIP; esp->esp_cmd = ESPCMD_NOP; /* * Reload configuration registers (cleared by * RESET_CHIP command). Reloading conf2 on an * ESP100 goofs it up, so out of paranoia we load * only the registers that exist. */ esp->esp_conf1 = sc->sc_conf1; if (sc->sc_esptype > ESP100) { /* 100A, 2XX */ esp->esp_conf2 = sc->sc_conf2; if (sc->sc_esptype > ESP100A) /* 2XX only */ esp->esp_conf3 = sc->sc_conf3; } esp->esp_ccf = sc->sc_ccf; esp->esp_timeout = sc->sc_sel_timeout; /* We set synch offset later. */ } if (how & RESET_SCSIBUS) { /* * The chip should retain most of its parameters * (including esp_ccf) across this kind of reset * (see section 3.5 of Emulex documentation). */ /* turn off scsi bus reset interrupts and reset scsi bus */ esp->esp_conf1 = sc->sc_conf1 | ESPCONF1_REPORT; esp->esp_cmd = ESPCMD_RESET_BUS; esp->esp_cmd = ESPCMD_NOP; DELAY(100000); /* ??? */ (void)esp->esp_intr; esp->esp_conf1 = sc->sc_conf1; } sc->sc_needclear = 0xff;}/* * Reset the SCSI bus and, optionally, all attached targets. */voidesphbareset(hba, resetunits) struct hba_softc *hba; int resetunits;{ register struct esp_softc *sc = (struct esp_softc *)hba; espreset(sc, RESET_SCSIBUS); if (resetunits) scsi_reset_units(&sc->sc_hba);}/* * Reset the esp, after an Sbus reset. * Also resets corresponding dma chip. * * THIS ROUTINE MIGHT GO AWAY */voidespsbreset(dev) struct device *dev;{ struct esp_softc *sc = (struct esp_softc *)dev; if (sc->sc_dc) { printf(" %s %s", sc->sc_dc->dc_dev.dv_xname, sc->sc_dev.dv_xname); esphbareset(&sc->sc_hba, 1); }}/* * Log an error. */static voidesperror(sc, err) register struct esp_softc *sc; const char *err;{ int stat; stat = sc->sc_espstat; printf("%s target %d cmd 0x%x (%s): %s:\n\\tstat=%b (%s) step=%x dmacsr=%b fflags=%x intr=%b\n", sc->sc_dev.dv_xname, sc->sc_targ, sc->sc_curcdb->cdb_bytes[0], espstates[sc->sc_state], err, stat, ESPSTAT_BITS, espphases[stat & ESPSTAT_PHASE], sc->sc_espstep, sc->sc_dmacsr, sc->sc_dc->dc_dmafmt, sc->sc_espfflags, sc->sc_espintr, ESPINTR_BITS);}/* * Issue a select, loading command into the FIFO. * Return nonzero on error, 0 if OK. * Sets state to `selecting'; espact() will sequence state FSM. */voidespselect(sc, targ, cdb) register struct esp_softc *sc; register int targ; register struct scsi_cdb *cdb;{ register volatile struct espreg *esp; register int i, cmdlen; sc->sc_targ = targ; sc->sc_state = S_SEL; sc->sc_curcdb = cdb; sc->sc_sentcmd = 0; sc->sc_stat[0] = 0xff; /* ??? */ sc->sc_msg[0] = 0xff; /* ??? */ /* * Try to talk to target. * Synch offset 0 => asynchronous transfer. */ esp = sc->sc_esp; esp->esp_id = targ; esp->esp_syncoff = 0; /* * Stuff the command bytes into the fifo. * Select without attention since we do not do disconnect yet. */ cmdlen = SCSICMDLEN(cdb->cdb_bytes[0]); for (i = 0; i < cmdlen; i++) esp->esp_fifo = cdb->cdb_bytes[i]; esp->esp_cmd = ESPCMD_SEL_NATN; /* the rest is done elsewhere */}/* * Sequence through the SCSI state machine. Return the action to take. * * Most of the work happens here. * * There are three interrupt sources: * -- ESP interrupt request (typically, some device wants something). * -- DMA memory error. * -- DMA byte count has reached 0 (we do not often want this one but * can only turn it off in rev 2 DMA chips, it seems). * DOES THIS OCCUR AT ALL HERE? THERE IS NOTHING TO HANDLE IT! */static intespact(sc) register struct esp_softc *sc;{ register volatile struct espreg *esp; register volatile struct dmareg *dma; register int reg, i, resid, newstate; register struct scsi_cdb *cdb; dma = sc->sc_dma; /* check various error conditions, using as little code as possible */ if (sc->sc_dmacsr & DMA_EP) { esperror(sc, "DMA error"); DMAWAIT(dma); dma->dma_csr |= DMA_FLUSH; return (ACT_ERROR); } reg = sc->sc_espstat; if (reg & ESPSTAT_GE) { /* * This often occurs when there is no target. * (See DSC code below.) */ if (sc->sc_espintr & ESPINTR_DSC && sc->sc_state == S_SEL && sc->sc_probing) { sc->sc_probing = PROBE_NO_TARGET; return (ACT_RESET); }esperror(sc, "DIAG: gross error (ignored)"); } if (reg & ESPSTAT_PE) { esperror(sc, "parity error"); return (ACT_RESET); } reg = sc->sc_espintr;#define ERR (ESPINTR_SBR|ESPINTR_ILC|ESPINTR_RSL|ESPINTR_SAT|ESPINTR_SEL) if (reg & ERR) { if (reg & ESPINTR_SBR) esperror(sc, "scsi bus reset"); else if (reg & ESPINTR_ILC) esperror(sc, "illegal command (driver bug)"); else { printf("%s: target %d", sc->sc_dev.dv_xname, sc->sc_targ); if (reg & ESPINTR_RSL) printf(" tried to reselect;"); if (reg & ESPINTR_SAT) printf(" selected with ATN;"); if (reg & ESPINTR_SEL) printf(" selected us as target;"); printf("we do not allow this yet\n"); } return (ACT_ERROR); }#undef ERR esp = sc->sc_esp; /* * Disconnect currently only allowed in `final interrupt' states. */ if (reg & ESPINTR_DSC) { if (sc->sc_state == S_FI) return (ACT_DONE); /* * If we were doing a select just to test the existence * of the target, note that it did not respond; otherwise * gripe. */ if (sc->sc_state == S_SEL) { if (sc->sc_probing) { sc->sc_probing = PROBE_NO_TARGET; return (ACT_RESET); } } /* flush fifo, in case we were selecting or sending data */ esp->esp_cmd = ESPCMD_FLUSH_FIFO; DELAY(1); printf("%s: target %d not responding\n", sc->sc_dev.dv_xname, sc->sc_targ); return (ACT_ERROR); } /* * Okay, things are moving along. * What were we doing the last time we did something, * and did it complete normally? */ switch (sc->sc_state) { case S_SEL: /* * We were selecting. Arbitration and select are * complete (because ESPINTR_DSC was not set), but * there is no guarantee the command went out. */ if ((reg & (ESPINTR_SVC|ESPINTR_CMP)) != (ESPINTR_SVC|ESPINTR_CMP)) { esperror(sc, "selection failed"); return (ACT_RESET); } if (sc->sc_espstep == ESPSTEP_DONE) { sc->sc_sentcmd = 1; break; } if (sc->sc_espstep == 2) { /* * We got something other than command phase. * Just pretend things are normal; the * device will ask for the command later. */esperror(sc, "DIAG: esp step 2"); } else if (sc->sc_espstep == 3) { /* * Device entered command phase and then exited it * before we finished handing out the command. * Let this happen iff we are trying to clear the * target state. */esperror(sc, "DIAG: esp step 3"); if (!sc->sc_clearing) return (ACT_RESET); } else { printf("%s: mysterious esp step %d\n", sc->sc_dev.dv_xname, sc->sc_espstep); return (ACT_RESET); } /* * Part of the command may still be lodged in the FIFO. */ if (ESP_NFIFO(sc->sc_espfflags)) { esp->esp_cmd = ESPCMD_FLUSH_FIFO; DELAY(1); } break; case S_SVC: /* * We were waiting for phase change after stuffing the command * into the FIFO. Make sure it got out. */ if (ESP_NFIFO(sc->sc_espfflags)) {esperror(sc, "DIAG: CMDSVC, fifo not empty"); esp->esp_cmd = ESPCMD_FLUSH_FIFO; DELAY(1); } else sc->sc_sentcmd = 1; break; case S_DI: /* * We were doing DMA data in, and expecting a * transfer-count-zero interrupt or a phase change. * We got that; drain the pack register and handle * as for data out -- but ignore FIFO (it should be * empty, except for sync mode which we are not * using anyway). */ DMAWAIT(dma); dma->dma_csr |= DMA_DRAIN; DELAY(1); resid = 0; goto dma_data_done; case S_DO: /* * We were doing DMA data out. If there is data in the * FIFO, it is stuff that got DMAed out but never made * it to the device, so it counts as residual. */ if ((resid = ESP_NFIFO(sc->sc_espfflags)) != 0) { esp->esp_cmd = ESPCMD_FLUSH_FIFO; DELAY(1); }dma_data_done: if (sc->sc_dmaactive == 0) { esperror(sc, "dma done w/o dmaactive"); panic("espact"); } sc->sc_dmaactive = 0; /* Finish computing residual count. */ reg = esp->esp_tcl | (esp->esp_tch << 8); if (reg == 0 && (sc->sc_espstat & ESPSTAT_TC) == 0) reg = 65536; resid += reg; /* Compute xfer count (requested - resid). */ i = sc->sc_dmasize - resid; if (i < 0) { printf("%s: xfer resid (%d) > xfer req (%d)\n", sc->sc_dev.dv_xname, resid, sc->sc_dmasize); i = sc->sc_dmasize; /* forgiving... */ } /* If data came in we must flush cache. */ if (sc->sc_state == S_DI) cache_flush(sc->sc_dmaaddr, i);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -