scsi.c revision 1.1.1.1 1 /* $NetBSD: scsi.c,v 1.1.1.1 1998/06/09 07:53:06 dbj 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/scsipi/scsi_message.h>
35 #if 0
36 #include <next/next/prominfo.h>
37 #else
38 #include <next68k/next68k/nextrom.h>
39 #endif
40 #include "scsireg.h"
41 #include "dmareg.h"
42 #include "scsivar.h"
43
44 #include <lib/libsa/stand.h>
45
46 struct scsi_softc scsi_softc, *sc = &scsi_softc;
47 char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer;
48
49 int scsi_msgin(void);
50 int dma_start(char *addr, int len);
51 int dma_done(void);
52
53 #ifdef SCSI_DEBUG
54 #define DPRINTF(x) printf x;
55 #else
56 #define DPRINTF(x)
57 #endif
58
59 void
60 scsi_init(void)
61 {
62 volatile caddr_t sr;
63 struct dma_dev *dma;
64
65 sr = P_SCSI;
66 dma = (struct dma_dev *)P_SCSI_CSR;
67
68 dma_buffer = DMA_ALIGN(char *, the_dma_buffer);
69
70 P_FLOPPY[FLP_CTRL] &= ~FLC_82077_SEL; /* select SCSI chip */
71
72 /* first reset dma */
73 dma->dd_csr = DMACSR_RESET;
74 DELAY(200);
75 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_RESET;
76 DELAY(10);
77 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB;
78 DELAY(10);
79
80 /* then reset the SCSI chip */
81 sr[ESP_CMD] = ESPCMD_RSTCHIP;
82 sr[ESP_CMD] = ESPCMD_NOP;
83 DELAY(500);
84
85 /* now reset the SCSI bus */
86 sr[ESP_CMD] = ESPCMD_RSTSCSI;
87 DELAY(18000000); /* XXX should be about 2-3 seconds at least */
88
89 /* then reset the SCSI chip again and initialize it properly */
90 sr[ESP_CMD] = ESPCMD_RSTCHIP;
91 sr[ESP_CMD] = ESPCMD_NOP;
92 DELAY(500);
93 sr[ESP_CFG1] = ESPCFG1_SLOW | ESPCFG1_BUSID;
94 sr[ESP_CFG2] = 0;
95 sr[ESP_CCF] = 4; /* S5RCLKCONV_FACTOR(20); */
96 sr[ESP_TIMEOUT] = 152; /* S5RSELECT_TIMEOUT(20,250); */
97 sr[ESP_SYNCOFF] = 0;
98 sr[ESP_SYNCTP] = 5;
99 /*
100 sc->sc_intrstatus = sr->s5r_intrstatus;
101 sc->sc_intrstatus = sr->s5r_intrstatus;
102 */
103 sr[ESP_CFG1] = ESPCFG1_PARENB | ESPCFG1_BUSID;
104
105 sc->sc_state = SCSI_IDLE;
106 }
107
108 void
109 scsierror(char *error)
110 {
111 printf("scsierror: %s.\n", error);
112 }
113
114 short
115 scsi_getbyte(volatile caddr_t sr)
116 {
117 if ((sr[ESP_FFLAG] & ESPFIFO_FF) == 0)
118 {
119 printf("getbyte: no data!\n");
120 return -1;
121 }
122 return sr[ESP_FIFO];
123 }
124
125 int
126 scsi_wait_for_intr(void)
127 {
128 #if 0
129 extern struct prominfo *pi;
130 volitle int = pi->pi_intrstat; /* ### use constant? */
131 #else
132 extern char *mg;
133 #define MON(type, off) (*(type *)((u_int) (mg) + off))
134 volatile int *intrstat = MON(volatile int *,MG_intrstat);
135 #endif
136 int count;
137
138 for(count = 0; count < SCSI_TIMEOUT; count++)
139 if (*intrstat & SCSI_INTR)
140 return 0;
141
142 printf("scsiicmd: timed out.\n");
143 return -1;
144 }
145
146 int
147 scsiicmd(char target, char lun,
148 u_char *cbuf, int clen,
149 char *addr, int len)
150 {
151 volatile caddr_t sr;
152 int i;
153
154 DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen,
155 target, (long)addr, len));
156 sr = P_SCSI;
157
158 if (sc->sc_state != SCSI_IDLE) {
159 scsierror("scsiiscmd: bad state");
160 return EIO;
161 }
162 sc->sc_result = 0;
163
164 /* select target */
165 sr[ESP_CMD] = ESPCMD_FLUSH;
166 DELAY(10);
167 sr[ESP_SELID] = target;
168 sr[ESP_FIFO] = MSG_IDENTIFY(lun, 0);
169 for (i=0; i<clen; i++)
170 sr[ESP_FIFO] = cbuf[i];
171 sr[ESP_CMD] = ESPCMD_SELATN;
172 sc->sc_state = SCSI_SELECTING;
173
174 while(sc->sc_state != SCSI_DONE) {
175 if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */
176 return EIO;
177
178 if (sc->sc_state == SCSI_DMA)
179 {
180 /* registers are not valid on dma intr */
181 sc->sc_status = sc->sc_seqstep = sc->sc_intrstatus = 0;
182 DPRINTF(("scsiicmd: dma intr\n"));
183 } else {
184 /* scsi processing */
185 sc->sc_status = sr[ESP_STAT];
186 sc->sc_seqstep = sr[ESP_STEP];
187 sc->sc_intrstatus = sr[ESP_INTR];
188 DPRINTF(("scsiicmd: regs[intr=%x, stat=%x, step=%x]\n",
189 sc->sc_intrstatus, sc->sc_status, sc->sc_seqstep));
190 }
191
192 if (sc->sc_intrstatus & ESPINTR_SBR) {
193 scsierror("scsi bus reset");
194 return EIO;
195 }
196
197 if ((sc->sc_status & ESPSTAT_GE)
198 || (sc->sc_intrstatus & ESPINTR_ILL)) {
199 scsierror("software error");
200 return EIO;
201 }
202 if (sc->sc_status & ESPSTAT_PE)
203 {
204 scsierror("parity error");
205 return EIO;
206 }
207
208 switch(sc->sc_state)
209 {
210 case SCSI_SELECTING:
211 if (sc->sc_intrstatus & ESPINTR_DIS)
212 {
213 sc->sc_state = SCSI_IDLE;
214 return EUNIT; /* device not present */
215 }
216
217 #define ESPINTR_DONE (ESPINTR_BS | ESPINTR_FC)
218 if ((sc->sc_intrstatus & ESPINTR_DONE) != ESPINTR_DONE)
219 {
220 scsierror("selection failed");
221 return EIO;
222 }
223 sc->sc_state = SCSI_HASBUS;
224 break;
225 case SCSI_HASBUS:
226 if (sc->sc_intrstatus & ESPINTR_DIS)
227 {
228 scsierror("target disconnected");
229 return EIO;
230 }
231 break;
232 case SCSI_DMA:
233 if (sc->sc_intrstatus & ESPINTR_DIS)
234 {
235 scsierror("target disconnected");
236 return EIO;
237 }
238 if (dma_done() != 0)
239 return EIO;
240 continue;
241 case SCSI_CLEANUP:
242 if (sc->sc_intrstatus & ESPINTR_DIS)
243 {
244 sc->sc_state = SCSI_DONE;
245 continue;
246 }
247 DPRINTF(("hmm ... no disconnect on cleanup?\n"));
248 sc->sc_state = SCSI_DONE; /* maybe ... */
249 break;
250 }
251
252 /* transfer information now */
253 switch(sc->sc_status & ESPSTAT_PHASE)
254 {
255 case DATA_IN_PHASE:
256 if (dma_start(addr, len) != 0)
257 return EIO;
258 break;
259 case DATA_OUT_PHASE:
260 scsierror("data out phase not implemented");
261 return EIO;
262 case STATUS_PHASE:
263 DPRINTF(("status phase: "));
264 sr[ESP_CMD] = ESPCMD_ICCS;
265 sc->sc_result = scsi_getbyte(sr);
266 DPRINTF(("status is 0x%x.\n", sc->sc_result));
267 break;
268 case MSG_IN_PHASE:
269 if (scsi_msgin() != 0)
270 return EIO;
271 break;
272 default:
273 DPRINTF(("phase not implemented: 0x%x.\n",
274 sc->sc_status & ESPSTAT_PHASE));
275 scsierror("bad phase");
276 return EIO;
277 }
278 }
279
280 sc->sc_state = SCSI_IDLE;
281 return -sc->sc_result;
282 }
283
284 int
285 scsi_msgin(void)
286 {
287 volatile caddr_t sr;
288 u_char msg;
289
290 sr = P_SCSI;
291
292 msg = scsi_getbyte(sr);
293 if (msg)
294 {
295 printf("unexpected msg: 0x%x.\n",msg);
296 return -1;
297 }
298 if ((sc->sc_intrstatus & ESPINTR_FC) == 0)
299 {
300 printf("not function complete.\n");
301 return -1;
302 }
303 sc->sc_state = SCSI_CLEANUP;
304 sr[ESP_CMD] = ESPCMD_MSGOK;
305 return 0;
306 }
307
308 int
309 dma_start(char *addr, int len)
310 {
311 volatile caddr_t sr;
312 struct dma_dev *dma;
313
314
315 sr = P_SCSI;
316 dma = (struct dma_dev *)P_SCSI_CSR;
317
318 if (len > MAX_DMASIZE)
319 {
320 scsierror("dma too long");
321 return -1;
322 }
323
324 if (addr == NULL || len == 0)
325 {
326 #if 0 /* I'd take that as an error in my code */
327 DPRINTF(("hmm ... no dma requested.\n"));
328 sr[ESP_TCL] = 0;
329 sr[ESP_TCM] = 1;
330 sr[ESP_CMD] = ESPCMD_NOP;
331 sr[ESP_CMD] = ESPCMD_DMA | ESPCMD_TRPAD;
332 return 0;
333 #else
334 scsierror("unrequested dma");
335 return -1;
336 #endif
337 }
338
339 DPRINTF(("dma start: %lx, %d byte.\n", (long)addr, len));
340 sc->dma_addr = addr;
341 sc->dma_len = len;
342
343 dma->dd_csr = DMACSR_READ | DMACSR_RESET;
344 dma->dd_next_initbuf = dma_buffer;
345 dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len);
346 dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE;
347
348 sr[ESP_TCL] = len & 0xff;
349 sr[ESP_TCM] = len >> 8;
350 sr[ESP_CMD] = ESPCMD_NOP;
351 sr[ESP_CMD] = ESPCMD_DMA | ESPCMD_TRANS;
352 sr[ESP_DCTL] = ESPDCTL_20MHZ|ESPDCTL_INTENB|ESPDCTL_DMAMOD|ESPDCTL_DMARD;
353
354 sc->sc_state = SCSI_DMA;
355 return 0;
356 }
357
358 int
359 dma_done(void)
360 {
361 volatile caddr_t sr;
362 struct dma_dev *dma;
363 int count, state;
364
365 sr = P_SCSI;
366 dma = (struct dma_dev *)P_SCSI_CSR;
367
368 state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE
369 | DMACSR_SUPDATE | DMACSR_ENABLE);
370
371 count = sr[ESP_TCM]<<8 | sr[ESP_TCL];
372 DPRINTF(("dma state = 0x%x, remain = %d.\n", state, count));
373
374 if (state & DMACSR_ENABLE)
375 {
376 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
377 | ESPDCTL_DMARD | ESPDCTL_FLUSH;
378 DELAY(5);
379 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD
380 | ESPDCTL_DMARD;
381 DELAY(5);
382 return 0;
383 }
384
385 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB;
386 count = sr[ESP_TCM]<<8 | sr[ESP_TCL];
387 dma->dd_csr = DMACSR_RESET;
388
389 DPRINTF(("dma done. remain = %d, state = 0x%x.\n", count, state));
390
391 if (count != 0)
392 {
393 printf("WARNING: unexpected %d characters remain in dma\n",count);
394 scsierror("dma transfer incomplete");
395 #if 0
396 return -1;
397 #endif
398 }
399
400 if (state & DMACSR_COMPLETE)
401 {
402 bcopy(dma_buffer, sc->dma_addr, sc->dma_len);
403 sc->sc_state = SCSI_HASBUS;
404 return 0;
405 }
406 if (state & DMACSR_BUSEXC)
407 {
408 scsierror("dma failed");
409 return -1;
410 }
411 scsierror("dma not completed\n");
412
413 return -1;
414 }
415