1/*      $NetBSD: scsi.c,v 1.9 2007/03/05 18:06:09 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
47struct  scsi_softc scsi_softc, *sc = &scsi_softc;
48char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer;
49
50int scsi_msgin(void);
51int dma_start(char *addr, int len);
52int dma_done(void);
53
54void scsi_init(void);
55void scsierror(char *error);
56short scsi_getbyte(volatile uint8_t *sr);
57int scsi_wait_for_intr(void);
58int 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
70void
71scsi_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
119void
120scsierror(char *error)
121{
122    printf("scsierror: %s.\n", error);
123}
124
125short
126scsi_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
136int
137scsi_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
163int
164scsiicmd(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
314int
315scsi_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
338int
339dma_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
401int
402dma_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