Home | History | Annotate | Line # | Download | only in boot
scsi.c revision 1.10
      1 /*      $NetBSD: scsi.c,v 1.10 2008/03/30 16:28:08 he Exp $        */
      2 /*
      3  * Copyright (c) 1994, 1997 Rolf Grossmann
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *      This product includes software developed by Rolf Grossmann.
     17  * 4. The name of the author may not be used to endorse or promote products
     18  *    derived from this software without specific prior written permission
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/param.h>
     33 #include <next68k/dev/espreg.h>
     34 #include <dev/ic/ncr53c9xreg.h>
     35 #include <dev/scsipi/scsi_message.h>
     36 #if 0
     37 #include <next/next/prominfo.h>
     38 #else
     39 #include <next68k/next68k/nextrom.h>
     40 #endif
     41 #include "scsireg.h"
     42 #include "dmareg.h"
     43 #include "scsivar.h"
     44 
     45 #include <lib/libsa/stand.h>
     46 
     47 struct  scsi_softc scsi_softc, *sc = &scsi_softc;
     48 char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer;
     49 
     50 int scsi_msgin(void);
     51 int dma_start(char *addr, int len);
     52 int dma_done(void);
     53 
     54 void scsi_init(void);
     55 void scsierror(char *error);
     56 short scsi_getbyte(volatile uint8_t *sr);
     57 int scsi_wait_for_intr(void);
     58 int scsiicmd(char target, char lun,
     59 	 u_char *cbuf, int clen, char *addr, int *len);
     60 
     61 #define NDPRINTF(x)
     62 #define PRINTF(x)
     63 /* printf x; */
     64 #ifdef xSCSI_DEBUG
     65 #define DPRINTF(x) printf x;
     66 #else
     67 #define DPRINTF(x)
     68 #endif
     69 
     70 void
     71 scsi_init(void)
     72 {
     73     volatile uint8_t *sr;
     74     struct dma_dev *dma;
     75 
     76     sr = P_SCSI;
     77     dma = (struct dma_dev *)P_SCSI_CSR;
     78 
     79     dma_buffer = DMA_ALIGN(char *, the_dma_buffer);
     80 
     81     P_FLOPPY[FLP_CTRL] &= ~FLC_82077_SEL;	/* select SCSI chip */
     82 
     83     /* first reset DMA */
     84     dma->dd_csr        = DMACSR_RESET;
     85     DELAY(200);
     86     sr[ESP_DCTL]       = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_RESET;
     87     DELAY(10);
     88     sr[ESP_DCTL]       = ESPDCTL_20MHZ | ESPDCTL_INTENB;
     89     DELAY(10);
     90 
     91     /* then reset the SCSI chip */
     92     sr[NCR_CMD]        = NCRCMD_RSTCHIP;
     93     sr[NCR_CMD]        = NCRCMD_NOP;
     94     DELAY(500);
     95 
     96     /* now reset the SCSI bus */
     97     sr[NCR_CMD]        = NCRCMD_RSTSCSI;
     98     DELAY(4000000);	/* XXX should be about 2-3 seconds at least */
     99 
    100     /* then reset the SCSI chip again and initialize it properly */
    101     sr[NCR_CMD]        = NCRCMD_RSTCHIP;
    102     sr[NCR_CMD]        = NCRCMD_NOP;
    103     DELAY(500);
    104     sr[NCR_CFG1]       = NCRCFG1_SLOW | NCRCFG1_BUSID;
    105     sr[NCR_CFG2]       = 0;
    106     sr[NCR_CCF]        = 4; /* S5RCLKCONV_FACTOR(20); */
    107     sr[NCR_TIMEOUT]    = 152; /* S5RSELECT_TIMEOUT(20,250); */
    108     sr[NCR_SYNCOFF]    = 0;
    109     sr[NCR_SYNCTP]     = 5;
    110    /*
    111     sc->sc_intrstatus  = sr->s5r_intrstatus;
    112     sc->sc_intrstatus  = sr->s5r_intrstatus;
    113     */
    114     sr[NCR_CFG1]       = NCRCFG1_PARENB | NCRCFG1_BUSID;
    115 
    116     sc->sc_state       = SCSI_IDLE;
    117 }
    118 
    119 void
    120 scsierror(char *error)
    121 {
    122     printf("scsierror: %s.\n", error);
    123 }
    124 
    125 short
    126 scsi_getbyte(volatile uint8_t *sr)
    127 {
    128     if ((sr[NCR_FFLAG] & NCRFIFO_FF) == 0)
    129     {
    130 	printf("getbyte: no data!\n");
    131 	return -1;
    132     }
    133     return sr[NCR_FIFO];
    134 }
    135 
    136 int
    137 scsi_wait_for_intr(void)
    138 {
    139 #if 0
    140   extern struct prominfo *pi;
    141   volitle int = pi->pi_intrstat; /* ### use constant? */
    142 #else
    143   extern char *mg;
    144 #define	MON(type, off) (*(type *)((u_int) (mg) + off))
    145   volatile int *intrstat = MON(volatile int *,MG_intrstat);
    146 #ifdef SCSI_DEBUG
    147 /*   volatile int *intrmask = MON(volatile int *,MG_intrmask); */
    148 #endif
    149 #endif
    150     int count;
    151 
    152     for(count = 0; count < SCSI_TIMEOUT; count++) {
    153 			NDPRINTF(("  *intrstat = 0x%x\t*intrmask = 0x%x\n",*intrstat,*intrmask));
    154 
    155 	if (*intrstat & SCSI_INTR)
    156 	    return 0;
    157 		}
    158 
    159     printf("scsiicmd: timed out.\n");
    160     return -1;
    161 }
    162 
    163 int
    164 scsiicmd(char target, char lun,
    165 	 u_char *cbuf, int clen,
    166 	 char *addr, int *len)
    167 {
    168     volatile uint8_t *sr;
    169     int i;
    170 
    171     DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen,
    172 	     target, (long)addr, *len));
    173     sr = P_SCSI;
    174 
    175     if (sc->sc_state != SCSI_IDLE) {
    176         scsierror("scsiiscmd: bad state");
    177 	return EIO;
    178     }
    179     sc->sc_result = 0;
    180 
    181     /* select target */
    182     sr[NCR_CMD]   = NCRCMD_FLUSH;
    183     DELAY(10);
    184     sr[NCR_SELID] = target;
    185     sr[NCR_FIFO]  = MSG_IDENTIFY(lun, 0);
    186     for (i=0; i<clen; i++)
    187 	sr[NCR_FIFO] = cbuf[i];
    188     sr[NCR_CMD]   = NCRCMD_SELATN;
    189     sc->sc_state  = SCSI_SELECTING;
    190 
    191     while(sc->sc_state != SCSI_DONE) {
    192 	if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */
    193 	    return EIO;
    194 
    195 	if (sc->sc_state == SCSI_DMA)
    196 	{
    197 	    /* registers are not valid on DMA intr */
    198 	    sc->sc_status = sc->sc_seqstep = sc->sc_intrstatus = 0;
    199 	    DPRINTF(("scsiicmd: DMA intr\n"));
    200 	    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD;
    201 	}
    202 
    203 	/* scsi processing */
    204 	sc->sc_status     = sr[NCR_STAT];
    205 	sc->sc_seqstep    = sr[NCR_STEP];
    206 	sc->sc_intrstatus = sr[NCR_INTR];
    207     redo:
    208 	DPRINTF(("scsiicmd: regs[intr=%x, stat=%x, step=%x]\n",
    209 		 sc->sc_intrstatus, sc->sc_status, sc->sc_seqstep));
    210 
    211 	if (sc->sc_intrstatus & NCRINTR_SBR) {
    212 	    scsierror("scsi bus reset");
    213 	    return EIO;
    214 	}
    215 
    216 	if ((sc->sc_status & NCRSTAT_GE)
    217 	    || (sc->sc_intrstatus & NCRINTR_ILL)) {
    218 	    scsierror("software error");
    219 	    return EIO;
    220 	}
    221 	if (sc->sc_status & NCRSTAT_PE)
    222 	{
    223 	    scsierror("parity error");
    224 	    return EIO;
    225 	}
    226 
    227 	switch(sc->sc_state)
    228 	{
    229 	  case SCSI_SELECTING:
    230 	      if (sc->sc_intrstatus & NCRINTR_DIS)
    231 	      {
    232 		  sc->sc_state = SCSI_IDLE;
    233 		  return EUNIT;	/* device not present */
    234 	      }
    235 
    236 #define NCRINTR_DONE (NCRINTR_BS | NCRINTR_FC)
    237 	      if ((sc->sc_intrstatus & NCRINTR_DONE) != NCRINTR_DONE)
    238 	      {
    239 		  scsierror("selection failed");
    240 		  return EIO;
    241 	      }
    242 	      sc->sc_state = SCSI_HASBUS;
    243 	      break;
    244 	  case SCSI_HASBUS:
    245 	      if (sc->sc_intrstatus & NCRINTR_DIS)
    246 	      {
    247 		  scsierror("target disconnected");
    248 		  return EIO;
    249 	      }
    250 	      break;
    251 	  case SCSI_DMA:
    252 	      if (sc->sc_intrstatus & NCRINTR_DIS)
    253 	      {
    254 		  scsierror("target disconnected");
    255 		  return EIO;
    256 	      }
    257 	      *len = dma_done();
    258 	      if (*len < 0) {
    259 		      *len = 0;
    260 		      return EIO;
    261 	      }
    262 	      /* continue; */
    263 	      sc->sc_status     = sr[NCR_STAT];
    264 	      goto redo;
    265 	      break;
    266 	  case SCSI_CLEANUP:
    267 	      if (sc->sc_intrstatus & NCRINTR_DIS)
    268 	      {
    269 		  sc->sc_state = SCSI_DONE;
    270 		  continue;
    271 	      }
    272 	      DPRINTF(("hmm ... no disconnect on cleanup?\n"));
    273 	      sc->sc_state = SCSI_DONE;	/* maybe ... */
    274 	      break;
    275 	}
    276 
    277 	/* transfer information now */
    278 	switch(sc->sc_status & NCRSTAT_PHASE)
    279 	{
    280 	  case DATA_IN_PHASE:
    281 		  sr[NCR_CMD] = NCRCMD_FLUSH;
    282 	      if (dma_start(addr, *len) != 0)
    283 		  return EIO;
    284 	      break;
    285 	  case DATA_OUT_PHASE:
    286 	      scsierror("data out phase not implemented");
    287 	      return EIO;
    288 	  case STATUS_PHASE:
    289 	      DPRINTF(("status phase: "));
    290 	      sr[NCR_CMD] = NCRCMD_ICCS;
    291 	      sc->sc_result = scsi_getbyte(sr);
    292 	      DPRINTF(("status is 0x%x.\n", sc->sc_result));
    293 	      break;
    294 	  case MSG_IN_PHASE:
    295 		if ((sc->sc_intrstatus & NCRINTR_BS) != 0) {
    296 			sr[NCR_CMD] = NCRCMD_FLUSH;
    297 			sr[NCR_CMD] = NCRCMD_TRANS;
    298 		} else
    299 			if (scsi_msgin() != 0)
    300 				return EIO;
    301 		break;
    302 	  default:
    303 	      DPRINTF(("phase not implemented: 0x%x.\n",
    304 		      sc->sc_status & NCRSTAT_PHASE));
    305               scsierror("bad phase");
    306 	      return EIO;
    307 	}
    308     }
    309 
    310     sc->sc_state = SCSI_IDLE;
    311     return -sc->sc_result;
    312 }
    313 
    314 int
    315 scsi_msgin(void)
    316 {
    317     volatile uint8_t *sr;
    318     u_char msg;
    319 
    320     sr = P_SCSI;
    321 
    322     msg = scsi_getbyte(sr);
    323     if (msg)
    324     {
    325 	printf("unexpected msg: 0x%x.\n",msg);
    326 	return -1;
    327     }
    328     if ((sc->sc_intrstatus & NCRINTR_FC) == 0)
    329     {
    330 	printf("not function complete.\n");
    331 	return -1;
    332     }
    333     sc->sc_state = SCSI_CLEANUP;
    334     sr[NCR_CMD]  = NCRCMD_MSGOK;
    335     return 0;
    336 }
    337 
    338 int
    339 dma_start(char *addr, int len)
    340 {
    341     volatile uint8_t *sr;
    342     struct dma_dev *dma;
    343 
    344 
    345     sr = P_SCSI;
    346     dma = (struct dma_dev *)P_SCSI_CSR;
    347 
    348     if (len > MAX_DMASIZE)
    349     {
    350 	scsierror("DMA too long");
    351 	return -1;
    352     }
    353 
    354     if (addr == NULL || len == 0)
    355     {
    356 #if 0 /* I'd take that as an error in my code */
    357 	DPRINTF(("hmm ... no DMA requested.\n"));
    358 	sr[NCR_TCL] = 0;
    359 	sr[NCR_TCM] = 1;
    360 	sr[NCR_CMD] = NCRCMD_NOP;
    361 	sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_TRPAD;
    362 	return 0;
    363 #else
    364 	scsierror("unrequested DMA");
    365 	return -1;
    366 #endif
    367     }
    368 
    369     PRINTF(("DMA start: %lx, %d byte.\n", (long)addr, len));
    370 
    371     DPRINTF(("dma_bufffer: start: 0x%lx end: 0x%lx \n",
    372 				(long)dma_buffer,(long)DMA_ENDALIGN(char *, dma_buffer+len)));
    373 
    374     sc->dma_addr = addr;
    375     sc->dma_len = len;
    376 
    377     sr[NCR_TCL]  = len & 0xff;
    378     sr[NCR_TCM]  = len >> 8;
    379     sr[NCR_CMD]  = NCRCMD_DMA | NCRCMD_NOP;
    380     sr[NCR_CMD]  = NCRCMD_DMA | NCRCMD_TRANS;
    381 
    382 #if 0
    383     dma->dd_csr = DMACSR_READ | DMACSR_RESET;
    384     dma->dd_next_initbuf = dma_buffer;
    385     dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len);
    386     dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE;
    387 #else
    388     dma->dd_csr = 0;
    389     dma->dd_csr = DMACSR_INITBUF | DMACSR_READ | DMACSR_RESET;
    390     dma->dd_next = dma_buffer;
    391     dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len);
    392     dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE;
    393 #endif
    394 
    395     sr[ESP_DCTL] = ESPDCTL_20MHZ|ESPDCTL_INTENB|ESPDCTL_DMAMOD|ESPDCTL_DMARD;
    396 
    397     sc->sc_state = SCSI_DMA;
    398     return 0;
    399 }
    400 
    401 int
    402 dma_done(void)
    403 {
    404     volatile uint8_t *sr;
    405     struct dma_dev *dma;
    406     int resid, state;
    407     int flushcount = 0;
    408 
    409     sr = P_SCSI;
    410     dma = (struct dma_dev *)P_SCSI_CSR;
    411 
    412     state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE
    413 			   | DMACSR_SUPDATE | DMACSR_ENABLE);
    414 
    415     sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD;
    416     resid = sr[NCR_TCM]<<8 | sr[NCR_TCL];
    417     DPRINTF(("DMA state = 0x%x, remain = %d.\n", state, resid));
    418 
    419     if (!(sr[NCR_FFLAG] & NCRFIFO_FF)) {
    420 	    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
    421 		    | ESPDCTL_DMARD;
    422 	    while (!(state & DMACSR_COMPLETE) && (state & DMACSR_ENABLE) && flushcount < 16)
    423 	    {
    424 
    425 		    DPRINTF(("DMA still enabled, flushing DCTL.\n"));
    426 
    427 		    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
    428 			    | ESPDCTL_DMARD | ESPDCTL_FLUSH;
    429 		    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
    430 			    | ESPDCTL_DMARD;
    431 
    432 		    flushcount++;
    433 		    state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE
    434 					   | DMACSR_SUPDATE | DMACSR_ENABLE);
    435 	    }
    436     }
    437     sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB;
    438     resid = (sr[NCR_TCM]<<8) + sr[NCR_TCL];
    439 
    440     dma->dd_csr = DMACSR_CLRCOMPLETE | DMACSR_RESET;
    441 
    442     DPRINTF(("DMA done. remain = %d, state = 0x%x, fifo = 0x%x.\n", resid, state, sr[NCR_FFLAG] & NCRFIFO_FF));
    443 
    444     if (resid != 0)
    445     {
    446 #if 1
    447       printf("WARNING: unexpected %d characters remain in DMA\n",resid);
    448 	scsierror("DMA transfer incomplete");
    449 	return -1;
    450 #endif
    451     }
    452 
    453     if (state & DMACSR_BUSEXC)
    454     {
    455 #if 0
    456 	scsierror("DMA failed");
    457 	return -1;
    458 #endif
    459     }
    460 
    461     sc->dma_len -= resid;
    462     if (sc->dma_len < 0)
    463 	    sc->dma_len = 0;
    464     memcpy(sc->dma_addr, dma_buffer, sc->dma_len);
    465     sc->sc_state = SCSI_HASBUS;
    466     DPRINTF(("DMA done. got %d.\n", sc->dma_len));
    467     return sc->dma_len;
    468 
    469     /* scsierror("DMA not completed\n"); */
    470 
    471     return 0;
    472 }
    473