xref: /netbsd/sys/arch/next68k/stand/boot/scsi.c (revision a8f41f0d)
1 /*      $NetBSD: scsi.c,v 1.14 2023/02/12 10:04:56 tsutsui 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 #include <next68k/next68k/nextrom.h>
37 #include "scsireg.h"
38 #include "dmareg.h"
39 #include "scsivar.h"
40 
41 #include <lib/libsa/stand.h>
42 
43 #include "samachdep.h"
44 
45 struct  scsi_softc scsi_softc, *sc = &scsi_softc;
46 char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer;
47 
48 int scsi_msgin(void);
49 int dma_start(char *addr, int len);
50 int dma_done(void);
51 
52 void scsierror(char *error);
53 short scsi_getbyte(volatile uint8_t *sr);
54 int scsi_wait_for_intr(void);
55 
56 #define NDPRINTF(x)
57 #define PRINTF(x)
58 /* printf x; */
59 #ifdef SCSI_DEBUG
60 #define DPRINTF(x) printf x;
61 #else
62 #define DPRINTF(x)
63 #endif
64 
65 void
scsi_init(void)66 scsi_init(void)
67 {
68     volatile uint8_t *sr;
69     struct dma_dev *dma;
70 
71     sr = P_SCSI;
72     dma = (struct dma_dev *)P_SCSI_CSR;
73 
74     dma_buffer = DMA_ALIGN(char *, the_dma_buffer);
75 
76     P_FLOPPY[FLP_CTRL] &= ~FLC_82077_SEL;	/* select SCSI chip */
77 
78     /* first reset DMA */
79     dma->dd_csr        = DMACSR_RESET;
80     DELAY(200);
81     sr[ESP_DCTL]       = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_RESET;
82     DELAY(10);
83     sr[ESP_DCTL]       = ESPDCTL_20MHZ | ESPDCTL_INTENB;
84     DELAY(10);
85 
86     /* then reset the SCSI chip */
87     sr[NCR_CMD]        = NCRCMD_RSTCHIP;
88     sr[NCR_CMD]        = NCRCMD_NOP;
89     DELAY(500);
90 
91     /* now reset the SCSI bus */
92     sr[NCR_CMD]        = NCRCMD_RSTSCSI;
93     /* wait 2 seconds after SCSI bus reset */
94     DELAY(2 * 1000 * 1000);
95 
96     /* then reset the SCSI chip again and initialize it properly */
97     sr[NCR_CMD]        = NCRCMD_RSTCHIP;
98     sr[NCR_CMD]        = NCRCMD_NOP;
99     DELAY(500);
100     sr[NCR_CFG1]       = NCRCFG1_SLOW | NCRCFG1_BUSID;
101     sr[NCR_CFG2]       = 0;
102     sr[NCR_CCF]        = 4; /* S5RCLKCONV_FACTOR(20); */
103     sr[NCR_TIMEOUT]    = 152; /* S5RSELECT_TIMEOUT(20,250); */
104     sr[NCR_SYNCOFF]    = 0;
105     sr[NCR_SYNCTP]     = 5;
106    /*
107     sc->sc_intrstatus  = sr->s5r_intrstatus;
108     sc->sc_intrstatus  = sr->s5r_intrstatus;
109     */
110     sr[NCR_CFG1]       = NCRCFG1_PARENB | NCRCFG1_BUSID;
111 
112     sc->sc_state       = SCSI_IDLE;
113 }
114 
115 void
scsierror(char * error)116 scsierror(char *error)
117 {
118     printf("scsierror: %s.\n", error);
119 }
120 
121 short
scsi_getbyte(volatile uint8_t * sr)122 scsi_getbyte(volatile uint8_t *sr)
123 {
124     if ((sr[NCR_FFLAG] & NCRFIFO_FF) == 0)
125     {
126 	printf("getbyte: no data!\n");
127 	return -1;
128     }
129     return sr[NCR_FIFO];
130 }
131 
132 int
scsi_wait_for_intr(void)133 scsi_wait_for_intr(void)
134 {
135 #define	MON(type, off) (*(type *)((u_int) (mg) + off))
136   volatile int *intrstat = MON(volatile int *,MG_intrstat);
137 #ifdef SCSI_DEBUG
138 /*   volatile int *intrmask = MON(volatile int *,MG_intrmask); */
139 #endif
140     int count;
141 
142     for(count = 0; count < SCSI_TIMEOUT; count++) {
143 			NDPRINTF(("  *intrstat = 0x%x\t*intrmask = 0x%x\n",*intrstat,*intrmask));
144 
145 	if (*intrstat & SCSI_INTR)
146 	    return 0;
147 		}
148 
149     printf("scsiicmd: timed out.\n");
150     return -1;
151 }
152 
153 int
scsiicmd(char target,char lun,u_char * cbuf,int clen,char * addr,int * len)154 scsiicmd(char target, char lun,
155 	 u_char *cbuf, int clen,
156 	 char *addr, int *len)
157 {
158     volatile uint8_t *sr;
159     int i;
160 
161     DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen,
162 	     target, (long)addr, *len));
163     sr = P_SCSI;
164 
165     if (sc->sc_state != SCSI_IDLE) {
166         scsierror("scsiiscmd: bad state");
167 	return EIO;
168     }
169     sc->sc_result = 0;
170 
171     /* select target */
172     sr[NCR_CMD]   = NCRCMD_FLUSH;
173     DELAY(10);
174     sr[NCR_SELID] = target;
175     sr[NCR_FIFO]  = MSG_IDENTIFY(lun, 0);
176     for (i=0; i<clen; i++)
177 	sr[NCR_FIFO] = cbuf[i];
178     sr[NCR_CMD]   = NCRCMD_SELATN;
179     sc->sc_state  = SCSI_SELECTING;
180 
181     while(sc->sc_state != SCSI_DONE) {
182 	if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */
183 	    return EIO;
184 
185 	if (sc->sc_state == SCSI_DMA)
186 	{
187 	    /* registers are not valid on DMA intr */
188 	    sc->sc_status = sc->sc_seqstep = sc->sc_intrstatus = 0;
189 	    DPRINTF(("scsiicmd: DMA intr\n"));
190 	    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD;
191 	}
192 
193 	/* scsi processing */
194 	sc->sc_status     = sr[NCR_STAT];
195 	sc->sc_seqstep    = sr[NCR_STEP];
196 	sc->sc_intrstatus = sr[NCR_INTR];
197     redo:
198 	DPRINTF(("scsiicmd: regs[intr=%x, stat=%x, step=%x]\n",
199 		 sc->sc_intrstatus, sc->sc_status, sc->sc_seqstep));
200 
201 	if (sc->sc_intrstatus & NCRINTR_SBR) {
202 	    scsierror("scsi bus reset");
203 	    return EIO;
204 	}
205 
206 	if ((sc->sc_status & NCRSTAT_GE)
207 	    || (sc->sc_intrstatus & NCRINTR_ILL)) {
208 	    scsierror("software error");
209 	    return EIO;
210 	}
211 	if (sc->sc_status & NCRSTAT_PE)
212 	{
213 	    scsierror("parity error");
214 	    return EIO;
215 	}
216 
217 	switch(sc->sc_state)
218 	{
219 	  case SCSI_SELECTING:
220 	      if (sc->sc_intrstatus & NCRINTR_DIS)
221 	      {
222 		  sc->sc_state = SCSI_IDLE;
223 		  return EUNIT;	/* device not present */
224 	      }
225 
226 #define NCRINTR_DONE (NCRINTR_BS | NCRINTR_FC)
227 	      if ((sc->sc_intrstatus & NCRINTR_DONE) != NCRINTR_DONE)
228 	      {
229 		  scsierror("selection failed");
230 		  return EIO;
231 	      }
232 	      sc->sc_state = SCSI_HASBUS;
233 	      break;
234 	  case SCSI_HASBUS:
235 	      if (sc->sc_intrstatus & NCRINTR_DIS)
236 	      {
237 		  scsierror("target disconnected");
238 		  return EIO;
239 	      }
240 	      break;
241 	  case SCSI_DMA:
242 	      if (sc->sc_intrstatus & NCRINTR_DIS)
243 	      {
244 		  scsierror("target disconnected");
245 		  return EIO;
246 	      }
247 	      *len = dma_done();
248 	      if (*len < 0) {
249 		      *len = 0;
250 		      return EIO;
251 	      }
252 	      /* continue; */
253 	      sc->sc_status     = sr[NCR_STAT];
254 	      goto redo;
255 	      break;
256 	  case SCSI_CLEANUP:
257 	      if (sc->sc_intrstatus & NCRINTR_DIS)
258 	      {
259 		  sc->sc_state = SCSI_DONE;
260 		  continue;
261 	      }
262 	      DPRINTF(("hmm ... no disconnect on cleanup?\n"));
263 	      sc->sc_state = SCSI_DONE;	/* maybe ... */
264 	      break;
265 	}
266 
267 	/* transfer information now */
268 	switch(sc->sc_status & NCRSTAT_PHASE)
269 	{
270 	  case DATA_IN_PHASE:
271 		  sr[NCR_CMD] = NCRCMD_FLUSH;
272 	      if (dma_start(addr, *len) != 0)
273 		  return EIO;
274 	      break;
275 	  case DATA_OUT_PHASE:
276 	      scsierror("data out phase not implemented");
277 	      return EIO;
278 	  case STATUS_PHASE:
279 	      DPRINTF(("status phase: "));
280 	      sr[NCR_CMD] = NCRCMD_ICCS;
281 	      sc->sc_result = scsi_getbyte(sr);
282 	      DPRINTF(("status is 0x%x.\n", sc->sc_result));
283 	      break;
284 	  case MSG_IN_PHASE:
285 		if ((sc->sc_intrstatus & NCRINTR_BS) != 0) {
286 			sr[NCR_CMD] = NCRCMD_FLUSH;
287 			sr[NCR_CMD] = NCRCMD_TRANS;
288 		} else
289 			if (scsi_msgin() != 0)
290 				return EIO;
291 		break;
292 	  default:
293 	      DPRINTF(("phase not implemented: 0x%x.\n",
294 		      sc->sc_status & NCRSTAT_PHASE));
295               scsierror("bad phase");
296 	      return EIO;
297 	}
298     }
299 
300     sc->sc_state = SCSI_IDLE;
301     return -sc->sc_result;
302 }
303 
304 int
scsi_msgin(void)305 scsi_msgin(void)
306 {
307     volatile uint8_t *sr;
308     u_char msg;
309 
310     sr = P_SCSI;
311 
312     msg = scsi_getbyte(sr);
313     if (msg)
314     {
315 	printf("unexpected msg: 0x%x.\n",msg);
316 	return -1;
317     }
318     if ((sc->sc_intrstatus & NCRINTR_FC) == 0)
319     {
320 	printf("not function complete.\n");
321 	return -1;
322     }
323     sc->sc_state = SCSI_CLEANUP;
324     sr[NCR_CMD]  = NCRCMD_MSGOK;
325     return 0;
326 }
327 
328 int
dma_start(char * addr,int len)329 dma_start(char *addr, int len)
330 {
331     volatile uint8_t *sr;
332     struct dma_dev *dma;
333 
334     sr = P_SCSI;
335     dma = (struct dma_dev *)P_SCSI_CSR;
336 
337     if (len > MAX_DMASIZE)
338     {
339 	scsierror("DMA too long");
340 	return -1;
341     }
342 
343     if (addr == NULL || len == 0)
344     {
345 #if 0 /* I'd take that as an error in my code */
346 	DPRINTF(("hmm ... no DMA requested.\n"));
347 	sr[NCR_TCL] = 0;
348 	sr[NCR_TCM] = 1;
349 	sr[NCR_CMD] = NCRCMD_NOP;
350 	sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_TRPAD;
351 	return 0;
352 #else
353 	scsierror("unrequested DMA");
354 	return -1;
355 #endif
356     }
357 
358     PRINTF(("DMA start: %lx, %d byte.\n", (long)addr, len));
359 
360     DPRINTF(("dma_bufffer: start: 0x%lx end: 0x%lx \n",
361 				(long)dma_buffer,(long)DMA_ENDALIGN(char *, dma_buffer+len)));
362 
363     sc->dma_addr = addr;
364     sc->dma_len = len;
365 
366     sr[NCR_TCL]  = len & 0xff;
367     sr[NCR_TCM]  = len >> 8;
368     sr[NCR_CMD]  = NCRCMD_DMA | NCRCMD_NOP;
369     sr[NCR_CMD]  = NCRCMD_DMA | NCRCMD_TRANS;
370 
371 #if 0
372     dma->dd_csr = DMACSR_READ | DMACSR_RESET;
373     dma->dd_next_initbuf = dma_buffer;
374     dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len);
375     dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE;
376 #else
377     dma->dd_csr = 0;
378     dma->dd_csr = DMACSR_INITBUF | DMACSR_READ | DMACSR_RESET;
379     dma->dd_next = dma_buffer;
380     dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len);
381     dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE;
382 #endif
383 
384     sr[ESP_DCTL] = ESPDCTL_20MHZ|ESPDCTL_INTENB|ESPDCTL_DMAMOD|ESPDCTL_DMARD;
385 
386     sc->sc_state = SCSI_DMA;
387     return 0;
388 }
389 
390 int
dma_done(void)391 dma_done(void)
392 {
393     volatile uint8_t *sr;
394     struct dma_dev *dma;
395     int resid, state;
396     int flushcount = 0;
397 
398     sr = P_SCSI;
399     dma = (struct dma_dev *)P_SCSI_CSR;
400 
401     state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE
402 			   | DMACSR_SUPDATE | DMACSR_ENABLE);
403 
404     sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD;
405     resid = sr[NCR_TCM]<<8 | sr[NCR_TCL];
406     DPRINTF(("DMA state = 0x%x, remain = %d.\n", state, resid));
407 
408     if (!(sr[NCR_FFLAG] & NCRFIFO_FF)) {
409 	    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
410 		    | ESPDCTL_DMARD;
411 	    while (!(state & DMACSR_COMPLETE) && (state & DMACSR_ENABLE) && flushcount < 16)
412 	    {
413 
414 		    DPRINTF(("DMA still enabled, flushing DCTL.\n"));
415 
416 		    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
417 			    | ESPDCTL_DMARD | ESPDCTL_FLUSH;
418 		    sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
419 			    | ESPDCTL_DMARD;
420 
421 		    flushcount++;
422 		    state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE
423 					   | DMACSR_SUPDATE | DMACSR_ENABLE);
424 	    }
425     }
426     sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB;
427     resid = (sr[NCR_TCM]<<8) + sr[NCR_TCL];
428 
429     dma->dd_csr = DMACSR_CLRCOMPLETE | DMACSR_RESET;
430 
431     DPRINTF(("DMA done. remain = %d, state = 0x%x, fifo = 0x%x.\n", resid, state, sr[NCR_FFLAG] & NCRFIFO_FF));
432 
433     if (resid != 0)
434     {
435 #if 1
436       printf("WARNING: unexpected %d characters remain in DMA\n",resid);
437 	scsierror("DMA transfer incomplete");
438 	return -1;
439 #endif
440     }
441 
442     if (state & DMACSR_BUSEXC)
443     {
444 #if 0
445 	scsierror("DMA failed");
446 	return -1;
447 #endif
448     }
449 
450     sc->dma_len -= resid;
451     if (sc->dma_len < 0)
452 	    sc->dma_len = 0;
453     memcpy(sc->dma_addr, dma_buffer, sc->dma_len);
454     sc->sc_state = SCSI_HASBUS;
455     DPRINTF(("DMA done. got %d.\n", sc->dma_len));
456     return sc->dma_len;
457 
458     /* scsierror("DMA not completed\n"); */
459 
460     return 0;
461 }
462