📄 acornscsi.c
字号:
* Notes : this will only be one SG entry or less */staticvoid acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length){ SCp->ptr += length; SCp->this_residual -= length; if (SCp->this_residual == 0 && next_SCp(SCp) == 0) host->dma.xfer_done = 1;}/* * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : read data from DMA RAM * Params : host - host to transfer from * ptr - DRAM address * start_addr - host mem address * length - number of bytes to transfer * Notes : this will only be one SG entry or less */staticvoid acornscsi_data_read(AS_Host *host, char *ptr, unsigned int start_addr, unsigned int length){ extern void __acornscsi_in(int port, char *buf, int len); unsigned int page, offset, len = length; page = (start_addr >> 12); offset = start_addr & ((1 << 12) - 1); outb((page & 0x3f) | host->card.page_reg, host->card.io_page); while (len > 0) { unsigned int this_len; if (len + offset > (1 << 12)) this_len = (1 << 12) - offset; else this_len = len; __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; len -= this_len; if (offset == (1 << 12)) { offset = 0; page ++; outb((page & 0x3f) | host->card.page_reg, host->card.io_page); } } outb(host->card.page_reg, host->card.io_page);}/* * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : write data to DMA RAM * Params : host - host to transfer from * ptr - DRAM address * start_addr - host mem address * length - number of bytes to transfer * Notes : this will only be one SG entry or less */staticvoid acornscsi_data_write(AS_Host *host, char *ptr, unsigned int start_addr, unsigned int length){ extern void __acornscsi_out(int port, char *buf, int len); unsigned int page, offset, len = length; page = (start_addr >> 12); offset = start_addr & ((1 << 12) - 1); outb((page & 0x3f) | host->card.page_reg, host->card.io_page); while (len > 0) { unsigned int this_len; if (len + offset > (1 << 12)) this_len = (1 << 12) - offset; else this_len = len; __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; len -= this_len; if (offset == (1 << 12)) { offset = 0; page ++; outb((page & 0x3f) | host->card.page_reg, host->card.io_page); } } outb(host->card.page_reg, host->card.io_page);}/* ========================================================================================= * On-board DMA routines */#ifdef USE_DMAC/* * Prototype: void acornscsi_dmastop(AS_Host *host) * Purpose : stop all DMA * Params : host - host on which to stop DMA * Notes : This is called when leaving DATA IN/OUT phase, * or when interface is RESET */static inlinevoid acornscsi_dma_stop(AS_Host *host){ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON); dmac_clearintr(host->dma.io_intr_clear);#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));#endif}/* * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) * Purpose : setup DMA controller for data transfer * Params : host - host to setup * direction - data transfer direction * Notes : This is called when entering DATA I/O phase, not * while we're in a DATA I/O phase */staticvoid acornscsi_dma_setup(AS_Host *host, dmadir_t direction){ unsigned int address, length, mode; host->dma.direction = direction; dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON); if (direction == DMA_OUT) {#if (DEBUG & DEBUG_NO_WRITE) if (NO_WRITE & (1 << host->SCpnt->device->id)) { printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", host->host->host_no, acornscsi_target(host)); return; }#endif mode = DMAC_WRITE; } else mode = DMAC_READ; /* * Allocate some buffer space, limited to half the buffer size */ length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & (DMAC_BUFFER_SIZE - 1); /* * Transfer data to DMA memory */ if (direction == DMA_OUT) acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; dmac_write(host->dma.io_port, DMAC_TXCNTLO, length); dmac_write(host->dma.io_port, DMAC_TXCNTHI, length >> 8); dmac_write(host->dma.io_port, DMAC_TXADRLO, address); dmac_write(host->dma.io_port, DMAC_TXADRMD, address >> 8); dmac_write(host->dma.io_port, DMAC_TXADRHI, 0); dmac_write(host->dma.io_port, DMAC_MODECON, mode); dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));#endif host->dma.xfer_setup = 1; }}/* * Function: void acornscsi_dma_cleanup(AS_Host *host) * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct * Params : host - host to finish * Notes : This is called when a command is: * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT * : This must not return until all transfers are completed. */staticvoid acornscsi_dma_cleanup(AS_Host *host){ dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON); dmac_clearintr(host->dma.io_intr_clear); /* * Check for a pending transfer */ if (host->dma.xfer_required) { host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) acornscsi_data_read(host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length); } /* * Has a transfer been setup? */ if (host->dma.xfer_setup) { unsigned int transferred; host->dma.xfer_setup = 0;#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi"));#endif /* * Calculate number of bytes transferred from DMA. */ transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; if (host->dma.direction == DMA_IN) acornscsi_data_read(host, host->scsi.SCp.ptr, host->dma.start_addr, transferred); /* * Update SCSI pointers */ acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));#endif }}/* * Function: void acornscsi_dmacintr(AS_Host *host) * Purpose : handle interrupts from DMAC device * Params : host - host to process * Notes : If reading, we schedule the read to main memory & * allow the transfer to continue. * : If writing, we fill the onboard DMA memory from main * memory. * : Called whenever DMAC finished it's current transfer. */staticvoid acornscsi_dma_intr(AS_Host *host){ unsigned int address, length, transferred;#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));#endif dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_ON); dmac_clearintr(host->dma.io_intr_clear); /* * Calculate amount transferred via DMA */ transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; /* * Schedule DMA transfer off board */ if (host->dma.direction == DMA_IN) { host->dma.xfer_start = host->dma.start_addr; host->dma.xfer_length = transferred; host->dma.xfer_ptr = host->scsi.SCp.ptr; host->dma.xfer_required = 1; } acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); /* * Allocate some buffer space, limited to half the on-board RAM size */ length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & (DMAC_BUFFER_SIZE - 1); /* * Transfer data to DMA memory */ if (host->dma.direction == DMA_OUT) acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; dmac_write(host->dma.io_port, DMAC_TXCNTLO, length); dmac_write(host->dma.io_port, DMAC_TXCNTHI, length >> 8); dmac_write(host->dma.io_port, DMAC_TXADRLO, address); dmac_write(host->dma.io_port, DMAC_TXADRMD, address >> 8); dmac_write(host->dma.io_port, DMAC_TXADRHI, 0); dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF);#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));#endif } else { host->dma.xfer_setup = 0;#if 0 /* * If the interface still wants more, then this is an error. * We give it another byte, but we also attempt to raise an * attention condition. We continue giving one byte until * the device recognises the attention. */ if (dmac_read(host->dma.io_port, DMAC_STATUS) & STATUS_RQ0) { acornscsi_abortcmd(host, host->SCpnt->tag); dmac_write(host->dma.io_port, DMAC_TXCNTLO, 0); dmac_write(host->dma.io_port, DMAC_TXCNTHI, 0); dmac_write(host->dma.io_port, DMAC_TXADRLO, 0); dmac_write(host->dma.io_port, DMAC_TXADRMD, 0); dmac_write(host->dma.io_port, DMAC_TXADRHI, 0); dmac_write(host->dma.io_port, DMAC_MASKREG, MASK_OFF); }#endif }}/* * Function: void acornscsi_dma_xfer(AS_Host *host) * Purpose : transfer data between AcornSCSI and memory * Params : host - host to process */staticvoid acornscsi_dma_xfer(AS_Host *host){ host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) acornscsi_data_read(host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length);}/* * Function: void acornscsi_dma_adjust(AS_Host *host) * Purpose : adjust DMA pointers & count for bytes transferred to * SBIC but not SCSI bus. * Params : host - host to adjust DMA count for */staticvoid acornscsi_dma_adjust(AS_Host *host){ if (host->dma.xfer_setup) { signed long transferred;#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));#endif /* * Calculate correct DMA address - DMA is ahead of SCSI bus while * writing. * host->scsi.SCp.scsi_xferred is the number of bytes * actually transferred to/from the SCSI bus. * host->dma.transferred is the number of bytes transferred * over DMA since host->dma.start_addr was last set. * * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred * - host->dma.transferred */ transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; if (transferred < 0) printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", host->host->host_no, acornscsi_target(host), transferred); else if (transferred == 0) host->dma.xfer_setup = 0; else { transferred += host->dma.start_addr; dmac_write(host->dma.io_port, DMAC_TXADRLO, transferred); dmac_write(host->dma.io_port, DMAC_TXADRMD, transferred >> 8); dmac_write(host->dma.io_port, DMAC_TXADRHI, transferred >> 16);#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));#endif } }}#endif/* ========================================================================================= * Data I/O */static intacornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout){ unsigned int asr, timeout = max_timeout; int my_ptr = *ptr; while (my_ptr < len) { asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR); if (asr & ASR_DBR) { timeout = max_timeout; sbic_arm_write(host->scsi.io_port, SBIC_DATA, bytes[my_ptr++]); } else if (asr & ASR_INT) break; else if (--timeout == 0) break; udelay(1); } *ptr = my_ptr; return (timeout == 0) ? -1 : 0;}/* * Function: void acornscsi_sendcommand(AS_Host *host) * Purpose : send a command to a target * Params : host - host which is connected to target */static voidacornscsi_sendcommand(AS_Host *host){ Scsi_Cmnd *SCpnt = host->SCpnt; sbic_arm_write(host->scsi.io_port, SBIC_TRANSCNTH, 0); sbic_arm_writenext(host->scsi.io_port, 0); sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); acornscsi_sbic_issuecmd(host, CMND_XFERINFO); if (acornscsi_write_pio(host, SCpnt->cmnd, (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) printk("scsi%d: timeout while sending command\n", host->host->host_no); host->scsi.phase = PHASE_COMMAND;}staticvoid acornscsi_sendmessage(AS_Host *host){ unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); unsigned int msgnr; struct message *msg;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -