📄 asc.c
字号:
case DS_3MAX: default: asc->dmar = (volatile int *)(cp->pmax_addr + ASC_OFFSET_DMAR); asc->buff = (u_char *)(cp->pmax_addr + ASC_OFFSET_RAM); bufsiz = PER_TGT_DMA_SIZE; asc->dma_start = tb_dma_start; asc->dma_end = tb_dma_end; }; /* * Now for timing. The 3max has a 25Mhz tb whereas the 3min and * maxine are 12.5Mhz. */ switch (pmax_boardtype) { case DS_3MAX: case DS_3MAXPLUS: asc->min_period = ASC_MIN_PERIOD25; asc->max_period = ASC_MAX_PERIOD25; asc->ccf = ASC_CCF(25); asc->timeout_250 = ASC_TIMEOUT_250(25, asc->ccf); asc->tb_ticks = 10; break; case DS_3MIN: case DS_MAXINE: default: asc->min_period = ASC_MIN_PERIOD12; asc->max_period = ASC_MAX_PERIOD12; asc->ccf = ASC_CCF(13); asc->timeout_250 = ASC_TIMEOUT_250(13, asc->ccf); asc->tb_ticks = 20; break; }; asc->state = ASC_STATE_IDLE; asc->target = -1; regs = asc->regs; /* * Reset chip, fully. Note that interrupts are already enabled. */ s = splbio(); /* preserve our ID for now */ asc->myid = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; asc->myidmask = ~(1 << asc->myid); asc_reset(asc, regs); /* * Our SCSI id on the bus. * The user can set this via the prom on 3maxen/pmaxen. * If this changes it is easy to fix: make a default that * can be changed as boot arg. */#ifdef unneeded regs->asc_cnfg1 = (regs->asc_cnfg1 & ~ASC_CNFG1_MY_BUS_ID) | (scsi_initiator_id[unit] & 0x7);#endif id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; splx(s); /* * Statically partition the DMA buffer between targets. * This way we will eventually be able to attach/detach * drives on-fly. And 18k/target is plenty for normal use. */ /* * Give each target its own DMA buffer region. * We may want to try ping ponging buffers later. */ for (i = 0; i < ASC_NCMD; i++) { asc->st[i].dmaBufAddr = asc->buff + bufsiz * i; asc->st[i].dmaBufSize = bufsiz; } printf("asc%d at nexus0 csr 0x%x priority %d SCSI id %d\n", unit, cp->pmax_addr, cp->pmax_pri, id); return (1);}/* * Start activity on a SCSI device. * We maintain information on each device separately since devices can * connect/disconnect during an operation. */voidasc_start(scsicmd) register ScsiCmd *scsicmd; /* command to start */{ register struct scsi_device *sdp = scsicmd->sd; register asc_softc_t asc = &asc_softc[sdp->sd_ctlr]; int s; s = splbio(); /* * Check if another command is already in progress. * We may have to change this if we allow SCSI devices with * separate LUNs. */ if (asc->cmd[sdp->sd_drive]) { printf("asc%d: device %s busy at start\n", sdp->sd_ctlr, sdp->sd_driver->d_name); (*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY, scsicmd->buflen, 0); splx(s); } asc->cmd[sdp->sd_drive] = scsicmd; asc_startcmd(asc, sdp->sd_drive); splx(s);}static voidasc_reset(asc, regs) asc_softc_t asc; asc_regmap_t *regs;{ /* * Reset chip and wait till done */ regs->asc_cmd = ASC_CMD_RESET; MachEmptyWriteBuffer(); DELAY(25); /* spec says this is needed after reset */ regs->asc_cmd = ASC_CMD_NOP; MachEmptyWriteBuffer(); DELAY(25); /* * Set up various chip parameters */ regs->asc_ccf = asc->ccf; MachEmptyWriteBuffer(); DELAY(25); regs->asc_sel_timo = asc->timeout_250; /* restore our ID */ regs->asc_cnfg1 = asc->myid | ASC_CNFG1_P_CHECK; /* include ASC_CNFG2_SCSI2 if you want to allow SCSI II commands */ regs->asc_cnfg2 = /* ASC_CNFG2_RFB | ASC_CNFG2_SCSI2 | */ ASC_CNFG2_EPL; regs->asc_cnfg3 = 0; /* zero anything else */ ASC_TC_PUT(regs, 0); regs->asc_syn_p = asc->min_period; regs->asc_syn_o = 0; /* async for now */ MachEmptyWriteBuffer();}/* * Start a SCSI command on a target. */static voidasc_startcmd(asc, target) asc_softc_t asc; int target;{ register asc_regmap_t *regs; register ScsiCmd *scsicmd; register State *state; int len; /* * See if another target is currently selected on this SCSI bus. */ if (asc->target >= 0) return; regs = asc->regs; /* * If a reselection is in progress, it is Ok to ignore it since * the ASC will automatically cancel the command and flush * the FIFO if the ASC is reselected before the command starts. * If we try to use ASC_CMD_DISABLE_SEL, we can hang the system if * a reselect occurs before starting the command. */ asc->state = ASC_STATE_BUSY; asc->target = target; /* cache some pointers */ scsicmd = asc->cmd[target]; state = &asc->st[target];#ifdef DEBUG if (asc_debug > 1) { printf("asc_startcmd: %s target %d cmd %x len %d\n", scsicmd->sd->sd_driver->d_name, target, scsicmd->cmd[0], scsicmd->buflen); }#endif /* * Init the chip and target state. */ state->flags = state->flags & DID_SYNC; state->error = 0; state->script = (script_t *)0; state->msg_out = SCSI_NO_OP; /* * Copy command data to the DMA buffer. */ len = scsicmd->cmdlen; state->dmalen = len; bcopy(scsicmd->cmd, state->dmaBufAddr, len); /* check for simple SCSI command with no data transfer */ if ((state->buflen = scsicmd->buflen) == 0) { /* check for sync negotiation */ if ((scsicmd->flags & SCSICMD_USE_SYNC) && !(state->flags & DID_SYNC)) { asc->script = &asc_scripts[SCRIPT_TRY_SYNC]; state->flags |= TRY_SYNC; } else asc->script = &asc_scripts[SCRIPT_SIMPLE]; state->buf = (char *)0; } else if (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) { asc->script = &asc_scripts[SCRIPT_DATA_OUT]; state->buf = scsicmd->buf; state->flags |= DMA_OUT; } else { asc->script = &asc_scripts[SCRIPT_DATA_IN]; state->buf = scsicmd->buf; state->flags |= DMA_IN; }#ifdef DEBUG asc_debug_cmd = scsicmd->cmd[0]; if (scsicmd->cmd[0] == SCSI_READ_EXT) { asc_debug_bn = (scsicmd->cmd[2] << 24) | (scsicmd->cmd[3] << 16) | (scsicmd->cmd[4] << 8) | scsicmd->cmd[5]; asc_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8]; } asc_logp->status = PACK(asc - asc_softc, 0, 0, asc_debug_cmd); asc_logp->target = asc->target; asc_logp->state = asc->script - asc_scripts; asc_logp->msg = SCSI_DIS_REC_IDENTIFY; asc_logp->resid = scsicmd->buflen; if (++asc_logp >= &asc_log[NLOG]) asc_logp = asc_log;#endif /* preload the FIFO with the message to be sent */ regs->asc_fifo = SCSI_DIS_REC_IDENTIFY; MachEmptyWriteBuffer(); /* initialize the DMA */ (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_WRITE); ASC_TC_PUT(regs, len); readback(regs->asc_cmd); regs->asc_dbus_id = target; readback(regs->asc_dbus_id); regs->asc_syn_p = state->sync_period; readback(regs->asc_syn_p); regs->asc_syn_o = state->sync_offset; readback(regs->asc_syn_o); if (state->flags & TRY_SYNC) regs->asc_cmd = ASC_CMD_SEL_ATN_STOP; else regs->asc_cmd = ASC_CMD_SEL_ATN | ASC_CMD_DMA; readback(regs->asc_cmd);}/* * Interrupt routine * Take interrupts from the chip * * Implementation: * Move along the current command's script if * all is well, invoke error handler if not. */voidasc_intr(unit) int unit;{ register asc_softc_t asc = &asc_softc[unit]; register asc_regmap_t *regs = asc->regs; register State *state; register script_t *scpt; register int ss, ir, status; /* collect ephemeral information */ status = regs->asc_status;again: ss = regs->asc_ss; ir = regs->asc_intr; /* this resets the previous two */ scpt = asc->script;#ifdef DEBUG asc_logp->status = PACK(unit, status, ss, ir); asc_logp->target = (asc->state == ASC_STATE_BUSY) ? asc->target : -1; asc_logp->state = scpt - asc_scripts; asc_logp->msg = -1; asc_logp->resid = 0; if (++asc_logp >= &asc_log[NLOG]) asc_logp = asc_log; if (asc_debug > 2) printf("asc_intr: status %x ss %x ir %x cond %d:%x\n", status, ss, ir, scpt - asc_scripts, scpt->condition);#endif /* check the expected state */ if (SCRIPT_MATCH(ir, status) == scpt->condition) { /* * Perform the appropriate operation, then proceed. */ if ((*scpt->action)(asc, status, ss, ir)) { regs->asc_cmd = scpt->command; readback(regs->asc_cmd); asc->script = scpt->next; } goto done; } /* * Check for parity error. * Hardware will automatically set ATN * to request the device for a MSG_OUT phase. */ if (status & ASC_CSR_PE) { printf("asc%d: SCSI device %d: incomming parity error seen\n", asc - asc_softc, asc->target); asc->st[asc->target].flags |= PARITY_ERR; } /* * Check for gross error. * Probably a bug in a device driver. */ if (status & ASC_CSR_GE) { printf("asc%d: SCSI device %d: gross error\n", asc - asc_softc, asc->target); goto abort; } /* check for message in or out */ if ((ir & ~ASC_INT_FC) == ASC_INT_BS) { register int len, fifo; state = &asc->st[asc->target]; switch (ASC_PHASE(status)) { case ASC_PHASE_DATAI: case ASC_PHASE_DATAO: ASC_TC_GET(regs, len); fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n", state->buflen, state->dmalen, len, fifo); goto abort; case ASC_PHASE_MSG_IN: break; case ASC_PHASE_MSG_OUT: /* * Check for parity error. * Hardware will automatically set ATN * to request the device for a MSG_OUT phase. */ if (state->flags & PARITY_ERR) { state->flags &= ~PARITY_ERR; state->msg_out = SCSI_MESSAGE_PARITY_ERROR; /* reset message in counter */ state->msglen = 0; } else state->msg_out = SCSI_NO_OP; regs->asc_fifo = state->msg_out; regs->asc_cmd = ASC_CMD_XFER_INFO; readback(regs->asc_cmd); goto done; case ASC_PHASE_STATUS: /* probably an error in the SCSI command */ asc->script = &asc_scripts[SCRIPT_GET_STATUS]; regs->asc_cmd = ASC_CMD_I_COMPLETE; readback(regs->asc_cmd); goto done; default: goto abort; } if (state->script) goto abort; /* check for DMA in progress */ ASC_TC_GET(regs, len); fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; /* flush any data in the FIFO */ if (fifo) { if (state->flags & DMA_OUT) len += fifo; else if (state->flags & DMA_IN) { u_char *cp; printf("asc_intr: IN: dmalen %d len %d fifo %d\n", state->dmalen, len, fifo); /* XXX */ len += fifo; cp = state->dmaBufAddr + (state->dmalen - len); while (fifo-- > 0) *cp++ = regs->asc_fifo; } else printf("asc_intr: dmalen %d len %d fifo %d\n", state->dmalen, len, fifo); /* XXX */ regs->asc_cmd = ASC_CMD_FLUSH; readback(regs->asc_cmd); DELAY(2); } if (len && (state->flags & DMA_IN_PROGRESS)) { /* save number of bytes still to be sent or received */ state->dmaresid = len; state->flags &= ~DMA_IN_PROGRESS;#ifdef DEBUG if (asc_logp == asc_log) asc_log[NLOG - 1].resid = len; else asc_logp[-1].resid = len;#endif /* setup state to resume to */ if (state->flags & DMA_IN) { /* * Since the ASC_CNFG3_SRB bit of the * cnfg3 register bit is not set, * we just transferred an extra byte. * Since we can't resume on an odd byte * boundary, we copy the valid data out * and resume DMA at the start address. */ if (len & 1) { printf("asc_intr: msg in len %d (fifo %d)\n", len, fifo); /* XXX */ len = state->dmalen - len; goto do_in; } state->script = &asc_scripts[SCRIPT_RESUME_DMA_IN]; } else if (state->flags & DMA_OUT) state->script = &asc_scripts[SCRIPT_RESUME_DMA_OUT]; else state->script = asc->script; } else if (state->flags & DMA_IN) { if (len) printf("asc_intr: 1: len %d (fifo %d)\n", len, fifo); /* XXX */ /* setup state to resume to */ if (state->flags & DMA_IN_PROGRESS) { len = state->dmalen; state->flags &= ~DMA_IN_PROGRESS; do_in: (*asc->dma_end)(asc, state, ASCDMA_READ); bcopy(state->dmaBufAddr, state->buf, len); state->buf += len; state->buflen -= len; } if (state->buflen) state->script = &asc_scripts[SCRIPT_RESUME_IN]; else state->script = &asc_scripts[SCRIPT_RESUME_NO_DATA]; } else if (state->flags & DMA_OUT) { if (len) printf("asc_intr: 2: len %d (fifo %d)\n", len, fifo); /* XXX */ /* * If this is the last chunk, the next expected * state is to get status. */ if (state->flags & DMA_IN_PROGRESS) { state->flags &= ~DMA_IN_PROGRESS; (*asc->dma_end)(asc, state, ASCDMA_WRITE); len = state->dmalen; state->buf += len; state->buflen -= len; } if (state->buflen) state->script = &asc_scripts[SCRIPT_RESUME_OUT]; else state->script = &asc_scripts[SCRIPT_RESUME_NO_DATA]; } else if (asc->script == &asc_scripts[SCRIPT_SIMPLE]) state->script = &asc_scripts[SCRIPT_RESUME_NO_DATA]; else state->script = asc->script; /* setup to receive a message */ asc->script = &asc_scripts[SCRIPT_MSG_IN]; state->msglen = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -