Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / arch / next68k / stand / boot / scsi.c
blob390ac1c7ee865e649ce0f91d1a611d11a2607cf0
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.
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
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.
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"
45 #include <lib/libsa/stand.h>
47 struct scsi_softc scsi_softc, *sc = &scsi_softc;
48 char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer;
50 int scsi_msgin(void);
51 int dma_start(char *addr, int len);
52 int dma_done(void);
54 void scsi_init(void);
55 void scsierror(char *error);
56 short scsi_getbyte(volatile uint8_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);
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
70 void
71 scsi_init(void)
73 volatile uint8_t *sr;
74 struct dma_dev *dma;
76 sr = P_SCSI;
77 dma = (struct dma_dev *)P_SCSI_CSR;
79 dma_buffer = DMA_ALIGN(char *, the_dma_buffer);
81 P_FLOPPY[FLP_CTRL] &= ~FLC_82077_SEL; /* select SCSI chip */
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);
91 /* then reset the SCSI chip */
92 sr[NCR_CMD] = NCRCMD_RSTCHIP;
93 sr[NCR_CMD] = NCRCMD_NOP;
94 DELAY(500);
96 /* now reset the SCSI bus */
97 sr[NCR_CMD] = NCRCMD_RSTSCSI;
98 DELAY(4000000); /* XXX should be about 2-3 seconds at least */
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;
111 sc->sc_intrstatus = sr->s5r_intrstatus;
112 sc->sc_intrstatus = sr->s5r_intrstatus;
114 sr[NCR_CFG1] = NCRCFG1_PARENB | NCRCFG1_BUSID;
116 sc->sc_state = SCSI_IDLE;
119 void
120 scsierror(char *error)
122 printf("scsierror: %s.\n", error);
125 short
126 scsi_getbyte(volatile uint8_t *sr)
128 if ((sr[NCR_FFLAG] & NCRFIFO_FF) == 0)
130 printf("getbyte: no data!\n");
131 return -1;
133 return sr[NCR_FIFO];
137 scsi_wait_for_intr(void)
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;
152 for(count = 0; count < SCSI_TIMEOUT; count++) {
153 NDPRINTF((" *intrstat = 0x%x\t*intrmask = 0x%x\n",*intrstat,*intrmask));
155 if (*intrstat & SCSI_INTR)
156 return 0;
159 printf("scsiicmd: timed out.\n");
160 return -1;
164 scsiicmd(char target, char lun,
165 u_char *cbuf, int clen,
166 char *addr, int *len)
168 volatile uint8_t *sr;
169 int i;
171 DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen,
172 target, (long)addr, *len));
173 sr = P_SCSI;
175 if (sc->sc_state != SCSI_IDLE) {
176 scsierror("scsiiscmd: bad state");
177 return EIO;
179 sc->sc_result = 0;
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;
191 while(sc->sc_state != SCSI_DONE) {
192 if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */
193 return EIO;
195 if (sc->sc_state == SCSI_DMA)
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;
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));
211 if (sc->sc_intrstatus & NCRINTR_SBR) {
212 scsierror("scsi bus reset");
213 return EIO;
216 if ((sc->sc_status & NCRSTAT_GE)
217 || (sc->sc_intrstatus & NCRINTR_ILL)) {
218 scsierror("software error");
219 return EIO;
221 if (sc->sc_status & NCRSTAT_PE)
223 scsierror("parity error");
224 return EIO;
227 switch(sc->sc_state)
229 case SCSI_SELECTING:
230 if (sc->sc_intrstatus & NCRINTR_DIS)
232 sc->sc_state = SCSI_IDLE;
233 return EUNIT; /* device not present */
236 #define NCRINTR_DONE (NCRINTR_BS | NCRINTR_FC)
237 if ((sc->sc_intrstatus & NCRINTR_DONE) != NCRINTR_DONE)
239 scsierror("selection failed");
240 return EIO;
242 sc->sc_state = SCSI_HASBUS;
243 break;
244 case SCSI_HASBUS:
245 if (sc->sc_intrstatus & NCRINTR_DIS)
247 scsierror("target disconnected");
248 return EIO;
250 break;
251 case SCSI_DMA:
252 if (sc->sc_intrstatus & NCRINTR_DIS)
254 scsierror("target disconnected");
255 return EIO;
257 *len = dma_done();
258 if (*len < 0) {
259 *len = 0;
260 return EIO;
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)
269 sc->sc_state = SCSI_DONE;
270 continue;
272 DPRINTF(("hmm ... no disconnect on cleanup?\n"));
273 sc->sc_state = SCSI_DONE; /* maybe ... */
274 break;
277 /* transfer information now */
278 switch(sc->sc_status & NCRSTAT_PHASE)
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;
310 sc->sc_state = SCSI_IDLE;
311 return -sc->sc_result;
315 scsi_msgin(void)
317 volatile uint8_t *sr;
318 u_char msg;
320 sr = P_SCSI;
322 msg = scsi_getbyte(sr);
323 if (msg)
325 printf("unexpected msg: 0x%x.\n",msg);
326 return -1;
328 if ((sc->sc_intrstatus & NCRINTR_FC) == 0)
330 printf("not function complete.\n");
331 return -1;
333 sc->sc_state = SCSI_CLEANUP;
334 sr[NCR_CMD] = NCRCMD_MSGOK;
335 return 0;
339 dma_start(char *addr, int len)
341 volatile uint8_t *sr;
342 struct dma_dev *dma;
345 sr = P_SCSI;
346 dma = (struct dma_dev *)P_SCSI_CSR;
348 if (len > MAX_DMASIZE)
350 scsierror("DMA too long");
351 return -1;
354 if (addr == NULL || len == 0)
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
369 PRINTF(("DMA start: %lx, %d byte.\n", (long)addr, len));
371 DPRINTF(("dma_bufffer: start: 0x%lx end: 0x%lx \n",
372 (long)dma_buffer,(long)DMA_ENDALIGN(char *, dma_buffer+len)));
374 sc->dma_addr = addr;
375 sc->dma_len = len;
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;
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
395 sr[ESP_DCTL] = ESPDCTL_20MHZ|ESPDCTL_INTENB|ESPDCTL_DMAMOD|ESPDCTL_DMARD;
397 sc->sc_state = SCSI_DMA;
398 return 0;
402 dma_done(void)
404 volatile uint8_t *sr;
405 struct dma_dev *dma;
406 int resid, state;
407 int flushcount = 0;
409 sr = P_SCSI;
410 dma = (struct dma_dev *)P_SCSI_CSR;
412 state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE
413 | DMACSR_SUPDATE | DMACSR_ENABLE);
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));
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)
425 DPRINTF(("DMA still enabled, flushing DCTL.\n"));
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;
432 flushcount++;
433 state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE
434 | DMACSR_SUPDATE | DMACSR_ENABLE);
437 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB;
438 resid = (sr[NCR_TCM]<<8) + sr[NCR_TCL];
440 dma->dd_csr = DMACSR_CLRCOMPLETE | DMACSR_RESET;
442 DPRINTF(("DMA done. remain = %d, state = 0x%x, fifo = 0x%x.\n", resid, state, sr[NCR_FFLAG] & NCRFIFO_FF));
444 if (resid != 0)
446 #if 1
447 printf("WARNING: unexpected %d characters remain in DMA\n",resid);
448 scsierror("DMA transfer incomplete");
449 return -1;
450 #endif
453 if (state & DMACSR_BUSEXC)
455 #if 0
456 scsierror("DMA failed");
457 return -1;
458 #endif
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;
469 /* scsierror("DMA not completed\n"); */
471 return 0;