📄 sim710.c
字号:
DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_700_OPC); NCR_write8(SIEN_REG_700, SIEN_PAR | SIEN_700_STO | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA);#ifdef CONFIG_TP34V_SCSI tpvic.loc_icr[irq_index[hostdata->chip]].icr = 0x30 | TP34V_SCSI0n1_IPL;#endif restore_flags(flags);}/* * Function : static void sim710_driver_init (struct Scsi_Host *host) * * Purpose : Initialize internal structures, as required on startup, or * after a SCSI bus reset. * * Inputs : host - pointer to this host adapter's structure */static voidsim710_driver_init (struct Scsi_Host *host){ struct sim710_hostdata *hostdata = (struct sim710_hostdata *) host->hostdata[0]; int i; hostdata->running = NULL; memcpy (hostdata->script, SCRIPT, sizeof(SCRIPT)); for (i = 0; i < PATCHES; i++) hostdata->script[LABELPATCHES[i]] += virt_to_bus(hostdata->script); patch_abs_32 (hostdata->script, 0, reselected_identify, virt_to_bus((void *)&(hostdata->reselected_identify))); patch_abs_32 (hostdata->script, 0, msgin_buf, virt_to_bus((void *)&(hostdata->msgin_buf[0]))); hostdata->state = STATE_INITIALISED; hostdata->negotiate = 0xff;}/* Handle incoming Synchronous data transfer request. If our negotiate * flag is set then this is a response to our request, otherwise it is * spurious request from the target. Don't really expect target initiated * SDTRs, because we always negotiate on the first command. Could still * get them though.. * The chip is currently paused with ACK asserted o the last byte of the * SDTR. * resa is the resume address if the message is in response to our outgoing * SDTR. Only possible on initial identify. * resb is the resume address if the message exchange is initiated by the * target. */static u32handle_sdtr (struct Scsi_Host * host, Scsi_Cmnd * cmd, u32 resa, u32 resb){ struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; struct sim710_target *targdata = hostdata->target + cmd->target; u32 resume_offset; if (resa && hostdata->negotiate & (1 << cmd->target)) { DEB(DEB_SYNC, printk("scsi%d: Response to host SDTR = %02x %02x\n", host->host_no, hostdata->msgin_buf[3], hostdata->msgin_buf[4])); /* We always issue an SDTR with the identify, so we must issue * the CDB next. */ resume_offset = resa; hostdata->negotiate &= ~(1 << cmd->target); } else { DEB(DEB_SYNC, printk("scsi%d: Target initiated SDTR = %02x %02x\n", host->host_no, hostdata->msgin_buf[3], hostdata->msgin_buf[4])); memcpy(targdata->dsa_msgout, async_message, sizeof(async_message)); targdata->dsa[DSA_MSGOUT] = sizeof(async_message); /* I guess the target could do this anytime; we have to send our * response, and then continue (sending the CDB if not already done). */ resume_offset = resb; } return resume_offset;}/* * Function : static int datapath_residual (Scsi_Host *host) * * Purpose : return residual data count of what's in the chip. * * Inputs : host - SCSI host */static intdatapath_residual (struct Scsi_Host *host) { int count, synchronous, sstat; unsigned int ddir; count = ((NCR_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) - (NCR_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK; synchronous = NCR_read8 (SXFER_REG) & SXFER_MO_MASK; ddir = NCR_read8 (CTEST0_REG_700) & CTEST0_700_DDIR; if (ddir) { /* Receive */ if (synchronous) count += (NCR_read8 (SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT; else if (NCR_read8 (SSTAT1_REG) & SSTAT1_ILF) ++count; } else { /* Send */ sstat = NCR_read8 (SSTAT1_REG); if (sstat & SSTAT1_OLF) ++count; if (synchronous && (sstat & SSTAT1_ORF)) ++count; } return count;}static u32handle_idd (struct Scsi_Host * host, Scsi_Cmnd * cmd){ struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; struct sim710_target *targdata = hostdata->target + cmd->target; u32 resume_offset = 0, index; index = (u32)((u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script); switch (index) { case Ent_wait_disc_complete/4 + 2: cmd->result = targdata->dsa_status[0]; SCSI_DONE(cmd); targdata->cur_cmd = NULL; resume_offset = Ent_reselect; break; case Ent_wait_disc2/4 + 2: /* Disconnect after command - just wait for a reselect */ targdata->resume_offset = Ent_resume_msgin2a; resume_offset = Ent_reselect; break; case Ent_wait_disc3/4 + 2: /* Disconnect after the data phase */ targdata->resume_offset = Ent_resume_msgin3a; resume_offset = Ent_reselect; break; case Ent_wait_disc1/4 + 2: /* Disconnect before command - not expected */ targdata->resume_offset = Ent_resume_msgin1a; resume_offset = Ent_reselect; break; default: printk("scsi%d: Unexpected Illegal Instruction, script[%04x]\n", host->host_no, index); sim710_errors++; /* resume_offset is zero, which will cause host reset */ } return resume_offset;}/* Handle a phase mismatch. */static u32handle_phase_mismatch (struct Scsi_Host * host, Scsi_Cmnd * cmd){ struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; struct sim710_target *targdata = hostdata->target + cmd->target; u32 resume_offset = 0, index; unsigned char sbcl; sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK; index = (u32)((u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script); DEB(DEB_PMM, printk("scsi%d: Phase mismatch, phase %s (%x) at script[0x%x]\n", host->host_no, sbcl_to_phase(sbcl), sbcl, index)); DEB(DEB_PMM, print_command(cmd->cmnd)); if (index == Ent_done_ident/4) { /* Sending initial message out - probably rejecting our sync * negotiation request. */ NCR_write8(SOCL_REG, 0); /* Negate ATN */ if (sbcl == SBCL_PHASE_MSGIN) resume_offset = Ent_resume_rej_ident; else if (sbcl == SBCL_PHASE_CMDOUT) { /* Some old devices (SQ555) switch to cmdout after the first * byte of an identify message, regardless of whether we * have more bytes to send! */ printk("scsi%d: Unexpected switch to CMDOUT during IDENTIFY\n", host->host_no); resume_offset = Ent_resume_cmd; } else { printk("scsi%d: Unexpected phase change to %s on initial msgout\n", host->host_no, sbcl_to_phase(sbcl)); /* resume_offset is zero, which will cause a host reset */ } hostdata->negotiate &= ~(1 << cmd->target); } else if (index > Ent_patch_input_data/4 && index < Ent_patch_output_data/4) { /* DataIn transfer phase */ u32 sg_id, oaddr, olen, naddr, nlen; int residual; sg_id = (index - Ent_patch_input_data/4 - 4) / 2; targdata->data_in_jump = hostdata->script[Ent_patch_input_data/4+1] = virt_to_bus(hostdata->script + Ent_patch_input_data/4 + sg_id * 2 + 2); olen = targdata->dsa[DSA_DATAIN + sg_id * 2]; oaddr = targdata->dsa[DSA_DATAIN + sg_id * 2 + 1]; residual = datapath_residual (host); if (residual) printk("scsi%d: Residual count %d on DataIn - NOT expected!!!", host->host_no, residual); naddr = NCR_read32(DNAD_REG) - residual; nlen = (NCR_read32(DBC_REG) & 0x00ffffff) + residual; DEB(DEB_PMM, printk("scsi%d: DIN sg %d, old %08x/%08x, new %08x/%08x (%d)\n", host->host_no, sg_id, oaddr, olen, naddr, nlen, residual)); if (oaddr+olen != naddr+nlen) { printk("scsi%d: PMM DIN counts error: 0x%x + 0x%x != 0x%x + 0x%x", host->host_no, oaddr, olen, naddr, nlen); } else { targdata->dsa[DSA_DATAIN + sg_id * 2] = nlen; targdata->dsa[DSA_DATAIN + sg_id * 2 + 1] = naddr; resume_offset = Ent_resume_pmm; } } else if (index > Ent_patch_output_data/4 && index <= Ent_end_data_trans/4) { /* Dataout transfer phase */ u32 sg_id, oaddr, olen, naddr, nlen; int residual; sg_id = (index - Ent_patch_output_data/4 - 4) / 2; targdata->data_out_jump = hostdata->script[Ent_patch_output_data/4+1] = virt_to_bus(hostdata->script + Ent_patch_output_data/4 + sg_id * 2 + 2); olen = targdata->dsa[DSA_DATAOUT + sg_id * 2]; oaddr = targdata->dsa[DSA_DATAOUT + sg_id * 2 + 1]; residual = datapath_residual (host); naddr = NCR_read32(DNAD_REG) - residual; nlen = (NCR_read32(DBC_REG) & 0x00ffffff) + residual; DEB(DEB_PMM, printk("scsi%d: DOUT sg %d, old %08x/%08x, new %08x/%08x (%d)\n", host->host_no, sg_id, oaddr, olen, naddr, nlen, residual)); if (oaddr+olen != naddr+nlen) { printk("scsi%d: PMM DOUT counts error: 0x%x + 0x%x != 0x%x + 0x%x", host->host_no, oaddr, olen, naddr, nlen); } else { targdata->dsa[DSA_DATAOUT + sg_id * 2] = nlen; targdata->dsa[DSA_DATAOUT + sg_id * 2 + 1] = naddr; resume_offset = Ent_resume_pmm; } } else { printk("scsi%d: Unexpected phase change to %s at index 0x%x\n", host->host_no, sbcl_to_phase(sbcl), index); /* resume_offset is zero, which will cause a host reset */ } /* Flush DMA FIFO */ NCR_write8 (CTEST8_REG, CTEST8_10_CLF); while (NCR_read8 (CTEST8_REG) & CTEST8_10_CLF); return resume_offset;}static u32handle_script_int(struct Scsi_Host * host, Scsi_Cmnd * cmd){ struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; struct sim710_target *targdata = hostdata->target + cmd->target; u32 dsps, resume_offset = 0; unsigned char sbcl; dsps = NCR_read32(DSPS_REG); switch (dsps) { case A_int_cmd_complete: cmd->result = targdata->dsa_status[0]; SCSI_DONE(cmd); targdata->cur_cmd = NULL; resume_offset = Ent_reselect; break; case A_int_msg_sdtr1: resume_offset = handle_sdtr(host, cmd, Ent_resume_msgin1a, Ent_resume_msgin1b); break; case A_int_msg_sdtr2: resume_offset = handle_sdtr(host, cmd, 0, Ent_resume_msgin2b); break; case A_int_msg_sdtr3: resume_offset = handle_sdtr(host, cmd, 0, Ent_resume_msgin3b); break; case A_int_disc1: /* Disconnect before command - not expected */ targdata->resume_offset = Ent_resume_msgin1a; resume_offset = Ent_reselect; break; case A_int_disc2: /* Disconnect after command - just wait for a reselect */ targdata->resume_offset = Ent_resume_msgin2a; resume_offset = Ent_reselect; break; case A_int_disc3: /* Disconnect after the data phase */ targdata->resume_offset = Ent_resume_msgin3a; resume_offset = Ent_reselect; break; case A_int_reselected: hostdata->script[Ent_patch_output_data/4+1] = targdata->data_out_jump; hostdata->script[Ent_patch_input_data/4+1] = targdata->data_in_jump; NCR_write32(DSA_REG, virt_to_bus(targdata->dsa)); resume_offset = targdata->resume_offset; break; case A_int_data_bad_phase: sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK; printk("scsi%d: int_data_bad_phase, phase %s (%x)\n", host->host_no, sbcl_to_phase(sbcl), sbcl); break; case A_int_bad_extmsg1a: case A_int_bad_extmsg1b: case A_int_bad_extmsg2a: case A_int_bad_extmsg2b: case A_int_bad_extmsg3a: case A_int_bad_extmsg3b: case A_int_bad_msg1: case A_int_bad_msg2: case A_int_bad_msg3: case A_int_cmd_bad_phase: case A_int_no_msgout1: case A_int_no_msgout2: case A_int_no_msgout3: case A_int_not_cmd_complete: case A_int_sel_no_ident: case A_int_sel_not_cmd: case A_int_status_not_msgin: case A_int_resel_not_msgin: case A_int_selected: case A_int_not_rej: default: sbcl = NCR_read8(SBCL_REG) & SBCL_PHASE_MASK; printk("scsi%d: Unimplemented script interrupt: %08x, phase %s\n", host->host_no, dsps, sbcl_to_phase(sbcl)); sim710_errors++; /* resume_offset is zero, which will cause a host reset */ } return resume_offset;}/* A quick wrapper for sim710_intr_handle to grab the spin lock */static voiddo_sim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); sim710_intr_handle(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_lock, flags);}/* A "high" level interrupt handler */static voidsim710_intr_handle(int irq, void *dev_id, struct pt_regs *regs){ unsigned int flags; struct Scsi_Host * host = (struct Scsi_Host *)dev_id; struct sim710_hostdata *hostdata = (struct sim710_hostdata *)host->hostdata[0]; Scsi_Cmnd * cmd; unsigned char istat, dstat; unsigned char sstat0; u32 dsps, resume_offset = 0; save_flags(flags); cli(); sim710_intrs++; while ((istat = NCR_read8(ISTAT_REG)) & (ISTAT_SIP|ISTAT_DIP)) { dsps = NCR_read32(DSPS_REG); hostdata->state = STATE_HALTED; sstat0 = dstat = 0; if (istat & ISTAT_SIP) { sstat0 = NCR_read8(SSTAT0_REG); } if (istat & ISTAT_DIP) { udelay(10); /* Some comment somewhere about 10cycles * between accesses to sstat0 and dstat ??? */ dstat = NCR_read8(DSTAT_REG); } DEB(DEB_INTS, printk("scsi%d: Int %d, istat %02x, sstat0 %02x " "dstat %02x, dsp [%04x], scratch %02x\n", host->host_no, sim710_intrs, istat, sstat0, dstat, (u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script, NCR_read32(SCRATCH_REG))); if ((dstat & DSTAT_SIR) && dsps == A_int_reselected) { /* Reselected. Identify the target from LCRC_REG, and * update current command. If we were trying to select * a device, then that command needs to go back on the * issue_queue for later. */ unsigned char lcrc = NCR_read8(LCRC_REG_10); int id = 0; if (!(lcrc & 0x7f)) { printk("scsi%d: Reselected with LCRC = %02x\n", host->host_no, lcrc); cmd = NULL; } else { while (!(lcrc & 1)) { id++; lcrc >>= 1; } DEB(DEB_DISC, printk("scsi%d: Reselected by ID %d\n", host->host_no, id)); if (hostdata->running) { /* Clear SIGP */ (void)NCR_read8(CTEST2_REG_700); DEB(DEB_DISC, printk("scsi%d: Select of %d interrupted " "by reselect from %d (%p)\n", host->host_no, hostdata->running->target, id, hostdata->target[id].cur_cmd)); cmd = hostdata->running; hostdata->target[cmd->target].cur_cmd = NULL; cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = cmd; } cmd = hostdata->running = hostdata->target[id].cur_cmd; } } else cmd = hostdata->running; if (!cmd) { printk("scsi%d: No active command!\n", host->host_no); printk("scsi%d: Int %d, istat %02x, sstat0 %02x " "dstat %02x, dsp [%04x], scratch %02x, dsps %08x\n", host->host_no, sim710_intrs, istat, sstat0, dstat, (u32 *)(bus_to_virt(NCR_read32(DSP_REG))) - hostdata->script, NCR_read32(SCRATCH_REG), dsps); /* resume_offset is zero, which will cause a host reset */ } else if (sstat0 & SSTAT0_700_STO) { DEB(DEB_TOUT, printk("scsi%d: Selection timeout\n", host->host_no)); cmd->result = DID_NO_CONNECT << 16; SCSI_DONE(cmd); hostdata->target[cmd->target].cur_cmd = NULL; resume_offset = Ent_reselect; } else if (dstat & DSTAT_SIR) resume_offset = handle_script_int(host, cmd); else if (sstat0 & SSTAT0_MA) { resume_offset = handle_phase_mismatch(host, cmd); } else if (sstat0 & (SSTAT0_MA|SSTAT0_SGE|SSTAT0_UDC|SSTAT0_RST|SSTAT0_PAR)) { printk("scsi%d: Serious error, sstat0 = %02x\n", host->host_no, sstat0); sim710_errors++; /* resume_offset is zero, which will cause a host reset */ } else if (dstat & (DSTAT_BF|DSTAT_ABRT|DSTAT_SSI|DSTAT_WTD)) { printk("scsi%d: Serious error, dstat = %02x\n", host->host_no, dstat); sim710_errors++; /* resume_offset is zero, which will cause a host reset */ } else if (dstat & DSTAT_IID) { /* This can be due to a quick reselect while doing a WAIT * DISCONNECT. */ resume_offset = handle_idd(host, cmd); } else { sim710_errors++; printk("scsi%d: Spurious interrupt!\n", host->host_no); /* resume_offset is zero, which will cause a host reset */ } } if (resume_offset) { if (resume_offset == Ent_reselect) { hostdata->running = NULL; hostdata->state = STATE_IDLE; } else hostdata->state = STATE_BUSY; DEB(DEB_RESUME, printk("scsi%d: Resuming at script[0x%x]\n", host->host_no, resume_offset/4));#ifdef DEBUG_LIMIT_INTS if (sim710_intrs < DEBUG_LIMIT_INTS)#endif NCR_write32(DSP_REG, virt_to_bus(hostdata->script+resume_offset/4)); if (resume_offset == Ent_reselect) run_process_issue_queue(hostdata); } else { printk("scsi%d: Failed to handle interrupt. Failing commands " "and resetting SCSI bus and chip\n", host->host_no); mdelay(4000); /* Give chance to read screen!! */ full_reset(host); } restore_flags(flags);}static voidrun_command (struct sim710_hostdata *hostdata, Scsi_Cmnd *cmd){ struct Scsi_Host *host = cmd->host; struct sim710_target *targdata = hostdata->target + cmd->target; int i, datain, dataout, sg_start; u32 *dip, *dop, dsa; DEB(DEB_CMND, printk("scsi%d: id%d starting ", host->host_no, cmd->target)); DEB(DEB_CMND, print_command(cmd->cmnd)); switch (cmd->cmnd[0]) { case INQUIRY: case MODE_SENSE: case READ_6: case READ_10: case READ_CAPACITY: case REQUEST_SENSE: case READ_BLOCK_LIMITS:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -