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