xref: /netbsd/sys/arch/next68k/stand/boot/scsi.c (revision c4a72b64)
1 /*      $NetBSD: scsi.c,v 1.5 2002/09/21 19:49:47 mycroft 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 caddr_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 caddr_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 caddr_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 caddr_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 caddr_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 caddr_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 caddr_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     bcopy(dma_buffer, sc->dma_addr, 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