📄 aic79xx.seq
字号:
/* * Functions to read data in Automatic PIO mode. * * An ACK is not sent on input from the target until SCSIDATL is read from. * So we wait until SCSIDATL is latched (the usual way), then read the data * byte directly off the bus using SCSIBUSL. When we have pulled the ATN * line, or we just want to acknowledge the byte, then we do a dummy read * from SCISDATL. The SCSI spec guarantees that the target will hold the * data byte on the bus until we send our ACK. * * The assumption here is that these are called in a particular sequence, * and that REQ is already set when inb_first is called. inb_{first,next} * use the same calling convention as inb. */inb_next: mov NONE,SCSIDAT; /*dummy read from latch to ACK*/inb_next_wait: /* * If there is a parity error, wait for the kernel to * see the interrupt and prepare our message response * before continuing. */ test SCSIPHASE, 0xFF jz .; test SSTAT1, SCSIPERR jnz inb_next_wait;inb_next_check_phase: and LASTPHASE, PHASE_MASK, SCSISIGI; cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;inb_first: clr DINDEX[1]; mov DINDEX,SINDEX; mov DINDIR,SCSIBUS ret; /*read byte directly from bus*/inb_last: mov NONE,SCSIDAT ret; /*dummy read from latch to ACK*/mk_mesg: mvi SCSISIGO, ATNO; mov MSG_OUT,SINDEX ret;SET_SRC_MODE M_DFF1;SET_DST_MODE M_DFF1;disable_ccsgen: test SG_STATE, FETCH_INPROG jz disable_ccsgen_fetch_done; clr CCSGCTL;disable_ccsgen_fetch_done: clr SG_STATE ret;service_fifo: /* * Do we have any prefetch left??? */ test SG_STATE, SEGS_AVAIL jnz idle_sg_avail; /* * Can this FIFO have access to the S/G cache yet? */ test CCSGCTL, SG_CACHE_AVAIL jz return; /* Did we just finish fetching segs? */ test CCSGCTL, CCSGDONE jnz idle_sgfetch_complete; /* Are we actively fetching segments? */ test CCSGCTL, CCSGENACK jnz return; /* * We fetch a "cacheline aligned" and sized amount of data * so we don't end up referencing a non-existant page. * Cacheline aligned is in quotes because the kernel will * set the prefetch amount to a reasonable level if the * cacheline size is unknown. */ bmov SGHADDR, SCB_RESIDUAL_SGPTR, 4; mvi SGHCNT, SG_PREFETCH_CNT; if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) { /* * Need two instruction between "touches" of SGHADDR. */ nop; } and SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR; mvi CCSGCTL, CCSGEN|CCSGRESET; or SG_STATE, FETCH_INPROG ret;idle_sgfetch_complete: /* * Guard against SG_CACHE_AVAIL activating during sg fetch * request in the other FIFO. */ test SG_STATE, FETCH_INPROG jz return; clr CCSGCTL; and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR; mvi SG_STATE, SEGS_AVAIL|LOADING_NEEDED;idle_sg_avail: /* Does the hardware have space for another SG entry? */ test DFSTATUS, PRELOAD_AVAIL jz return; /* * On the A, preloading a segment before HDMAENACK * comes true can clobber the shaddow address of the * first segment in the S/G FIFO. Wait until it is * safe to proceed. */ if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) == 0) { test DFCNTRL, HDMAENACK jz return; } if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) { bmov HADDR, CCSGRAM, 8; } else { bmov HADDR, CCSGRAM, 4; } bmov HCNT, CCSGRAM, 3; bmov SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1; if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) { and HADDR[4], SG_HIGH_ADDR_BITS, SCB_RESIDUAL_DATACNT[3]; } if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) { /* Skip 4 bytes of pad. */ add CCSGADDR, 4; }sg_advance: clr A; /* add sizeof(struct scatter) */ add SCB_RESIDUAL_SGPTR[0],SG_SIZEOF; adc SCB_RESIDUAL_SGPTR[1],A; adc SCB_RESIDUAL_SGPTR[2],A; adc SCB_RESIDUAL_SGPTR[3],A; mov SINDEX, SCB_RESIDUAL_SGPTR[0]; test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 3; or SINDEX, LAST_SEG; clr SG_STATE; mov SG_CACHE_PRE, SINDEX; if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0) { /* * Use SCSIENWRDIS so that SCSIEN is never * modified by this operation. */ or DFCNTRL, PRELOADEN|HDMAEN|SCSIENWRDIS; } else { or DFCNTRL, PRELOADEN|HDMAEN; } /* * Do we have another segment in the cache? */ add NONE, SG_PREFETCH_CNT_LIMIT, CCSGADDR; jnc return; and SG_STATE, ~SEGS_AVAIL ret;/* * Initialize the DMA address and counter from the SCB. */load_first_seg: bmov HADDR, SCB_DATAPTR, 11; and REG_ISR, ~SG_FULL_RESID, SCB_SGPTR[0]; test SCB_DATACNT[3], SG_LAST_SEG jz . + 2; or REG_ISR, LAST_SEG; mov SG_CACHE_PRE, REG_ISR; mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN); /* * Since we've are entering a data phase, we will * rely on the SCB_RESID* fields. Initialize the * residual and clear the full residual flag. */ and SCB_SGPTR[0], ~SG_FULL_RESID; bmov SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5; /* If we need more S/G elements, tell the idle loop */ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz . + 2; mvi SG_STATE, LOADING_NEEDED ret; clr SG_STATE ret;p_data_handle_xfer: call setjmp; test SG_STATE, LOADING_NEEDED jnz service_fifo;p_data_clear_handler: or LONGJMP_ADDR[1], INVALID_ADDR ret;p_data: test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed; SET_SEQINTCODE(PROTO_VIOLATION)p_data_allowed: test SEQ_FLAGS, DPHASE jz data_phase_initialize; /* * If we re-enter the data phase after going through another * phase, our transfer location has almost certainly been * corrupted by the interveining, non-data, transfers. Ask * the host driver to fix us up based on the transfer residual * unless we already know that we should be bitbucketing. */ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket; SET_SEQINTCODE(PDATA_REINIT) jmp data_phase_inbounds;p_data_bitbucket: /* * Turn on `Bit Bucket' mode, wait until the target takes * us to another phase, and then notify the host. */ mov SAVED_MODE, MODE_PTR; test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz bitbucket_not_m_dff; /* * Ensure that any FIFO contents are cleared out and the * FIFO free'd prior to starting the BITBUCKET. BITBUCKET * doesn't discard data already in the FIFO. */ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT; SET_MODE(M_SCSI, M_SCSI)bitbucket_not_m_dff: or SXFRCTL1,BITBUCKET; /* Wait for non-data phase. */ test SCSIPHASE, ~DATA_PHASE_MASK jz .; and SXFRCTL1, ~BITBUCKET; RESTORE_MODE(SAVED_MODE)SET_SRC_MODE M_DFF1;SET_DST_MODE M_DFF1; SET_SEQINTCODE(DATA_OVERRUN) jmp ITloop;data_phase_initialize: test SCB_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket; call load_first_seg;data_phase_inbounds: /* We have seen a data phase at least once. */ or SEQ_FLAGS, DPHASE; mov SAVED_MODE, MODE_PTR; test SG_STATE, LOADING_NEEDED jz data_group_dma_loop; call p_data_handle_xfer;data_group_dma_loop: /* * The transfer is complete if either the last segment * completes or the target changes phase. Both conditions * will clear SCSIEN. */ call idle_loop_service_fifos; call idle_loop_cchan; call idle_loop_gsfifo; RESTORE_MODE(SAVED_MODE) test DFCNTRL, SCSIEN jnz data_group_dma_loop;data_group_dmafinish: /* * The transfer has terminated either due to a phase * change, and/or the completion of the last segment. * We have two goals here. Do as much other work * as possible while the data fifo drains on a read * and respond as quickly as possible to the standard * messages (save data pointers/disconnect and command * complete) that usually follow a data phase. */ call calc_residual; /* * Go ahead and shut down the DMA engine now. */ test DFCNTRL, DIRECTION jnz data_phase_finish;data_group_fifoflush: if ((ahd->bugs & AHD_AUTOFLUSH_BUG) != 0) { or DFCNTRL, FIFOFLUSH; } /* * We have enabled the auto-ack feature. This means * that the controller may have already transferred * some overrun bytes into the data FIFO and acked them * on the bus. The only way to detect this situation is * to wait for LAST_SEG_DONE to come true on a completed * transfer and then test to see if the data FIFO is * non-empty. We know there is more data yet to transfer * if SG_LIST_NULL is not yet set, thus there cannot be * an overrun. */ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_finish; test SG_CACHE_SHADOW, LAST_SEG_DONE jz .; test DFSTATUS, FIFOEMP jnz data_phase_finish; /* Overrun */ jmp p_data;data_phase_finish: /* * If the target has left us in data phase, loop through * the dma code again. We will only loop if there is a * data overrun. */ if ((ahd->flags & AHD_TARGETROLE) != 0) { test SSTAT0, TARGET jnz data_phase_done; } if ((ahd->flags & AHD_INITIATORROLE) != 0) { test SSTAT1, REQINIT jz .; test SCSIPHASE, DATA_PHASE_MASK jnz p_data; }data_phase_done: /* Kill off any pending prefetch */ call disable_ccsgen; or LONGJMP_ADDR[1], INVALID_ADDR; if ((ahd->flags & AHD_TARGETROLE) != 0) { test SEQ_FLAGS, DPHASE_PENDING jz ITloop; /* and SEQ_FLAGS, ~DPHASE_PENDING; * For data-in phases, wait for any pending acks from the * initiator before changing phase. We only need to * send Ignore Wide Residue messages for data-in phases. test DFCNTRL, DIRECTION jz target_ITloop; test SSTAT1, REQINIT jnz .; test SCB_TASK_ATTRIBUTE, SCB_XFERLEN_ODD jz target_ITloop; SET_MODE(M_SCSI, M_SCSI) test NEGCONOPTS, WIDEXFER jz target_ITloop; */ /* * Issue an Ignore Wide Residue Message. mvi P_MESGIN|BSYO call change_phase; mvi MSG_IGN_WIDE_RESIDUE call target_outb; mvi 1 call target_outb; jmp target_ITloop; */ } else { jmp ITloop; }/* * We assume that, even though data may still be * transferring to the host, that the SCSI side of * the DMA engine is now in a static state. This * allows us to update our notion of where we are * in this transfer. * * If, by chance, we stopped before being able * to fetch additional segments for this transfer, * yet the last S/G was completely exhausted, * call our idle loop until it is able to load * another segment. This will allow us to immediately * pickup on the next segment on the next data phase. * * If we happened to stop on the last segment, then * our residual information is still correct from * the idle loop and there is no need to perform * any fixups. */residual_before_last_seg: test MDFFSTAT, SHVALID jnz sgptr_fixup; /* * Can never happen from an interrupt as the packetized * hardware will only interrupt us once SHVALID or * LAST_SEG_DONE. */ call idle_loop_service_fifos; RESTORE_MODE(SAVED_MODE) /* FALLTHROUGH */calc_residual: test SG_CACHE_SHADOW, LAST_SEG jz residual_before_last_seg; /* Record if we've consumed all S/G entries */ test MDFFSTAT, SHVALID jz . + 2; bmov SCB_RESIDUAL_DATACNT, SHCNT, 3 ret; or SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL ret;sgptr_fixup: /* * Fixup the residual next S/G pointer. The S/G preload * feature of the chip allows us to load two elements * in addition to the currently active element. We * store the bottom byte of the next S/G pointer in * the SG_CACHE_PTR register so we can restore the * correct value when the DMA completes. If the next * sg ptr value has advanced to the point where higher * bytes in the address have been affected, fix them * too. */ test SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done; test SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done; add SCB_RESIDUAL_SGPTR[1], -1; adc SCB_RESIDUAL_SGPTR[2], -1; adc SCB_RESIDUAL_SGPTR[3], -1;sgptr_fixup_done: and SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW; clr SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */ bmov SCB_RESIDUAL_DATACNT, SHCNT, 3 ret;export timer_isr: call issue_cmdcmplt; mvi CLRSEQINTSTAT, CLRSEQ_SWTMRTO; if ((ahd->bugs & AHD_SET_MODE_BUG) != 0) { /* * In H2A4, the mode pointer is not saved * for intvec2, but is restored on iret. * This can lead to the restoration of a * bogus mode ptr. Manually clear the * intmask bits and do a normal return * to compensate. */ and SEQINTCTL, ~(INTMASK2|INTMASK1) ret; } else { or SEQINTCTL, IRET ret; }export seq_isr: if ((ahd->features & AHD_RTI) == 0) { /* * On RevA Silicon, if the target returns us to data-out * after we have already trained for data-out, it is * possible for us to transition the free running clock to * data-valid before the required 100ns P1 setup time (8 P1 * assertions in fast-160 mode). This will only happen if * this L-Q is a continuation of a data transfer for which * we have already prefetched data into our FIFO (LQ/Data * followed by LQ/Data for the same write transaction). * This can cause some target implementations to miss the * first few data transfers on the bus. We detect this * situation by noticing that this is the first data transfer * after an LQ (LQIWORKONLQ true), that the data transfer is * a continuation of a transfer already setup in our FIFO * (SAVEPTRS interrupt), and that the transaction is a write * (DIRECTION set in DFCNTRL). The delay is performed by * disabling SCSIEN until we see the first REQ from the * target. * * First instruction in an ISR cannot be a branch on
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -