/* $NetBSD: scsi.c,v 1.1.1.1 1998/06/09 07:53:06 dbj Exp $ */ /* * Copyright (c) 1994, 1997 Rolf Grossmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Rolf Grossmann. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #if 0 #include #else #include #endif #include "scsireg.h" #include "dmareg.h" #include "scsivar.h" #include struct scsi_softc scsi_softc, *sc = &scsi_softc; char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer; int scsi_msgin(void); int dma_start(char *addr, int len); int dma_done(void); #ifdef SCSI_DEBUG #define DPRINTF(x) printf x; #else #define DPRINTF(x) #endif void scsi_init(void) { volatile caddr_t sr; struct dma_dev *dma; sr = P_SCSI; dma = (struct dma_dev *)P_SCSI_CSR; dma_buffer = DMA_ALIGN(char *, the_dma_buffer); P_FLOPPY[FLP_CTRL] &= ~FLC_82077_SEL; /* select SCSI chip */ /* first reset dma */ dma->dd_csr = DMACSR_RESET; DELAY(200); sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_RESET; DELAY(10); sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB; DELAY(10); /* then reset the SCSI chip */ sr[ESP_CMD] = ESPCMD_RSTCHIP; sr[ESP_CMD] = ESPCMD_NOP; DELAY(500); /* now reset the SCSI bus */ sr[ESP_CMD] = ESPCMD_RSTSCSI; DELAY(18000000); /* XXX should be about 2-3 seconds at least */ /* then reset the SCSI chip again and initialize it properly */ sr[ESP_CMD] = ESPCMD_RSTCHIP; sr[ESP_CMD] = ESPCMD_NOP; DELAY(500); sr[ESP_CFG1] = ESPCFG1_SLOW | ESPCFG1_BUSID; sr[ESP_CFG2] = 0; sr[ESP_CCF] = 4; /* S5RCLKCONV_FACTOR(20); */ sr[ESP_TIMEOUT] = 152; /* S5RSELECT_TIMEOUT(20,250); */ sr[ESP_SYNCOFF] = 0; sr[ESP_SYNCTP] = 5; /* sc->sc_intrstatus = sr->s5r_intrstatus; sc->sc_intrstatus = sr->s5r_intrstatus; */ sr[ESP_CFG1] = ESPCFG1_PARENB | ESPCFG1_BUSID; sc->sc_state = SCSI_IDLE; } void scsierror(char *error) { printf("scsierror: %s.\n", error); } short scsi_getbyte(volatile caddr_t sr) { if ((sr[ESP_FFLAG] & ESPFIFO_FF) == 0) { printf("getbyte: no data!\n"); return -1; } return sr[ESP_FIFO]; } int scsi_wait_for_intr(void) { #if 0 extern struct prominfo *pi; volitle int = pi->pi_intrstat; /* ### use constant? */ #else extern char *mg; #define MON(type, off) (*(type *)((u_int) (mg) + off)) volatile int *intrstat = MON(volatile int *,MG_intrstat); #endif int count; for(count = 0; count < SCSI_TIMEOUT; count++) if (*intrstat & SCSI_INTR) return 0; printf("scsiicmd: timed out.\n"); return -1; } int scsiicmd(char target, char lun, u_char *cbuf, int clen, char *addr, int len) { volatile caddr_t sr; int i; DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen, target, (long)addr, len)); sr = P_SCSI; if (sc->sc_state != SCSI_IDLE) { scsierror("scsiiscmd: bad state"); return EIO; } sc->sc_result = 0; /* select target */ sr[ESP_CMD] = ESPCMD_FLUSH; DELAY(10); sr[ESP_SELID] = target; sr[ESP_FIFO] = MSG_IDENTIFY(lun, 0); for (i=0; isc_state = SCSI_SELECTING; while(sc->sc_state != SCSI_DONE) { if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */ return EIO; if (sc->sc_state == SCSI_DMA) { /* registers are not valid on dma intr */ sc->sc_status = sc->sc_seqstep = sc->sc_intrstatus = 0; DPRINTF(("scsiicmd: dma intr\n")); } else { /* scsi processing */ sc->sc_status = sr[ESP_STAT]; sc->sc_seqstep = sr[ESP_STEP]; sc->sc_intrstatus = sr[ESP_INTR]; DPRINTF(("scsiicmd: regs[intr=%x, stat=%x, step=%x]\n", sc->sc_intrstatus, sc->sc_status, sc->sc_seqstep)); } if (sc->sc_intrstatus & ESPINTR_SBR) { scsierror("scsi bus reset"); return EIO; } if ((sc->sc_status & ESPSTAT_GE) || (sc->sc_intrstatus & ESPINTR_ILL)) { scsierror("software error"); return EIO; } if (sc->sc_status & ESPSTAT_PE) { scsierror("parity error"); return EIO; } switch(sc->sc_state) { case SCSI_SELECTING: if (sc->sc_intrstatus & ESPINTR_DIS) { sc->sc_state = SCSI_IDLE; return EUNIT; /* device not present */ } #define ESPINTR_DONE (ESPINTR_BS | ESPINTR_FC) if ((sc->sc_intrstatus & ESPINTR_DONE) != ESPINTR_DONE) { scsierror("selection failed"); return EIO; } sc->sc_state = SCSI_HASBUS; break; case SCSI_HASBUS: if (sc->sc_intrstatus & ESPINTR_DIS) { scsierror("target disconnected"); return EIO; } break; case SCSI_DMA: if (sc->sc_intrstatus & ESPINTR_DIS) { scsierror("target disconnected"); return EIO; } if (dma_done() != 0) return EIO; continue; case SCSI_CLEANUP: if (sc->sc_intrstatus & ESPINTR_DIS) { sc->sc_state = SCSI_DONE; continue; } DPRINTF(("hmm ... no disconnect on cleanup?\n")); sc->sc_state = SCSI_DONE; /* maybe ... */ break; } /* transfer information now */ switch(sc->sc_status & ESPSTAT_PHASE) { case DATA_IN_PHASE: if (dma_start(addr, len) != 0) return EIO; break; case DATA_OUT_PHASE: scsierror("data out phase not implemented"); return EIO; case STATUS_PHASE: DPRINTF(("status phase: ")); sr[ESP_CMD] = ESPCMD_ICCS; sc->sc_result = scsi_getbyte(sr); DPRINTF(("status is 0x%x.\n", sc->sc_result)); break; case MSG_IN_PHASE: if (scsi_msgin() != 0) return EIO; break; default: DPRINTF(("phase not implemented: 0x%x.\n", sc->sc_status & ESPSTAT_PHASE)); scsierror("bad phase"); return EIO; } } sc->sc_state = SCSI_IDLE; return -sc->sc_result; } int scsi_msgin(void) { volatile caddr_t sr; u_char msg; sr = P_SCSI; msg = scsi_getbyte(sr); if (msg) { printf("unexpected msg: 0x%x.\n",msg); return -1; } if ((sc->sc_intrstatus & ESPINTR_FC) == 0) { printf("not function complete.\n"); return -1; } sc->sc_state = SCSI_CLEANUP; sr[ESP_CMD] = ESPCMD_MSGOK; return 0; } int dma_start(char *addr, int len) { volatile caddr_t sr; struct dma_dev *dma; sr = P_SCSI; dma = (struct dma_dev *)P_SCSI_CSR; if (len > MAX_DMASIZE) { scsierror("dma too long"); return -1; } if (addr == NULL || len == 0) { #if 0 /* I'd take that as an error in my code */ DPRINTF(("hmm ... no dma requested.\n")); sr[ESP_TCL] = 0; sr[ESP_TCM] = 1; sr[ESP_CMD] = ESPCMD_NOP; sr[ESP_CMD] = ESPCMD_DMA | ESPCMD_TRPAD; return 0; #else scsierror("unrequested dma"); return -1; #endif } DPRINTF(("dma start: %lx, %d byte.\n", (long)addr, len)); sc->dma_addr = addr; sc->dma_len = len; dma->dd_csr = DMACSR_READ | DMACSR_RESET; dma->dd_next_initbuf = dma_buffer; dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len); dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE; sr[ESP_TCL] = len & 0xff; sr[ESP_TCM] = len >> 8; sr[ESP_CMD] = ESPCMD_NOP; sr[ESP_CMD] = ESPCMD_DMA | ESPCMD_TRANS; sr[ESP_DCTL] = ESPDCTL_20MHZ|ESPDCTL_INTENB|ESPDCTL_DMAMOD|ESPDCTL_DMARD; sc->sc_state = SCSI_DMA; return 0; } int dma_done(void) { volatile caddr_t sr; struct dma_dev *dma; int count, state; sr = P_SCSI; dma = (struct dma_dev *)P_SCSI_CSR; state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE | DMACSR_SUPDATE | DMACSR_ENABLE); count = sr[ESP_TCM]<<8 | sr[ESP_TCL]; DPRINTF(("dma state = 0x%x, remain = %d.\n", state, count)); if (state & DMACSR_ENABLE) { sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD | ESPDCTL_DMARD | ESPDCTL_FLUSH; DELAY(5); sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD | ESPDCTL_DMARD; DELAY(5); return 0; } sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB; count = sr[ESP_TCM]<<8 | sr[ESP_TCL]; dma->dd_csr = DMACSR_RESET; DPRINTF(("dma done. remain = %d, state = 0x%x.\n", count, state)); if (count != 0) { printf("WARNING: unexpected %d characters remain in dma\n",count); scsierror("dma transfer incomplete"); #if 0 return -1; #endif } if (state & DMACSR_COMPLETE) { bcopy(dma_buffer, sc->dma_addr, sc->dma_len); sc->sc_state = SCSI_HASBUS; return 0; } if (state & DMACSR_BUSEXC) { scsierror("dma failed"); return -1; } scsierror("dma not completed\n"); return -1; }