1 /* $NetBSD: scsi.c,v 1.9 2007/03/05 18:06:09 he Exp $ */
3 * Copyright (c) 1994, 1997 Rolf Grossmann
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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>
37 #include <next/next/prominfo.h>
39 #include <next68k/next68k/nextrom.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
;
51 int dma_start(char *addr
, int len
);
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
);
65 #define DPRINTF(x) printf x;
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 */
84 dma
->dd_csr
= DMACSR_RESET
;
86 sr
[ESP_DCTL
] = ESPDCTL_20MHZ
| ESPDCTL_INTENB
| ESPDCTL_RESET
;
88 sr
[ESP_DCTL
] = ESPDCTL_20MHZ
| ESPDCTL_INTENB
;
91 /* then reset the SCSI chip */
92 sr
[NCR_CMD
] = NCRCMD_RSTCHIP
;
93 sr
[NCR_CMD
] = NCRCMD_NOP
;
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
;
104 sr
[NCR_CFG1
] = NCRCFG1_SLOW
| NCRCFG1_BUSID
;
106 sr
[NCR_CCF
] = 4; /* S5RCLKCONV_FACTOR(20); */
107 sr
[NCR_TIMEOUT
] = 152; /* S5RSELECT_TIMEOUT(20,250); */
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
;
120 scsierror(char *error
)
122 printf("scsierror: %s.\n", error
);
126 scsi_getbyte(volatile uint8_t *sr
)
128 if ((sr
[NCR_FFLAG
] & NCRFIFO_FF
) == 0)
130 printf("getbyte: no data!\n");
137 scsi_wait_for_intr(void)
140 extern struct prominfo
*pi
;
141 volitle
int = pi
->pi_intrstat
; /* ### use constant? */
144 #define MON(type, off) (*(type *)((u_int) (mg) + off))
145 volatile int *intrstat
= MON(volatile int *,MG_intrstat
);
147 /* volatile int *intrmask = MON(volatile int *,MG_intrmask); */
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
)
159 printf("scsiicmd: timed out.\n");
164 scsiicmd(char target
, char lun
,
165 u_char
*cbuf
, int clen
,
166 char *addr
, int *len
)
168 volatile uint8_t *sr
;
171 DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf
, clen
,
172 target
, (long)addr
, *len
));
175 if (sc
->sc_state
!= SCSI_IDLE
) {
176 scsierror("scsiiscmd: bad state");
182 sr
[NCR_CMD
] = NCRCMD_FLUSH
;
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 ? */
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
];
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");
216 if ((sc
->sc_status
& NCRSTAT_GE
)
217 || (sc
->sc_intrstatus
& NCRINTR_ILL
)) {
218 scsierror("software error");
221 if (sc
->sc_status
& NCRSTAT_PE
)
223 scsierror("parity error");
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");
242 sc
->sc_state
= SCSI_HASBUS
;
245 if (sc
->sc_intrstatus
& NCRINTR_DIS
)
247 scsierror("target disconnected");
252 if (sc
->sc_intrstatus
& NCRINTR_DIS
)
254 scsierror("target disconnected");
263 sc
->sc_status
= sr
[NCR_STAT
];
267 if (sc
->sc_intrstatus
& NCRINTR_DIS
)
269 sc
->sc_state
= SCSI_DONE
;
272 DPRINTF(("hmm ... no disconnect on cleanup?\n"));
273 sc
->sc_state
= SCSI_DONE
; /* maybe ... */
277 /* transfer information now */
278 switch(sc
->sc_status
& NCRSTAT_PHASE
)
281 sr
[NCR_CMD
] = NCRCMD_FLUSH
;
282 if (dma_start(addr
, *len
) != 0)
286 scsierror("data out phase not implemented");
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
));
295 if ((sc
->sc_intrstatus
& NCRINTR_BS
) != 0) {
296 sr
[NCR_CMD
] = NCRCMD_FLUSH
;
297 sr
[NCR_CMD
] = NCRCMD_TRANS
;
299 if (scsi_msgin() != 0)
303 DPRINTF(("phase not implemented: 0x%x.\n",
304 sc
->sc_status
& NCRSTAT_PHASE
));
305 scsierror("bad phase");
310 sc
->sc_state
= SCSI_IDLE
;
311 return -sc
->sc_result
;
317 volatile uint8_t *sr
;
322 msg
= scsi_getbyte(sr
);
325 printf("unexpected msg: 0x%x.\n",msg
);
328 if ((sc
->sc_intrstatus
& NCRINTR_FC
) == 0)
330 printf("not function complete.\n");
333 sc
->sc_state
= SCSI_CLEANUP
;
334 sr
[NCR_CMD
] = NCRCMD_MSGOK
;
339 dma_start(char *addr
, int len
)
341 volatile uint8_t *sr
;
346 dma
= (struct dma_dev
*)P_SCSI_CSR
;
348 if (len
> MAX_DMASIZE
)
350 scsierror("DMA too long");
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"));
360 sr
[NCR_CMD
] = NCRCMD_NOP
;
361 sr
[NCR_CMD
] = NCRCMD_DMA
| NCRCMD_TRPAD
;
364 scsierror("unrequested DMA");
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
)));
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
;
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
;
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
;
395 sr
[ESP_DCTL
] = ESPDCTL_20MHZ
|ESPDCTL_INTENB
|ESPDCTL_DMAMOD
|ESPDCTL_DMARD
;
397 sc
->sc_state
= SCSI_DMA
;
404 volatile uint8_t *sr
;
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
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
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
));
447 printf("WARNING: unexpected %d characters remain in DMA\n",resid
);
448 scsierror("DMA transfer incomplete");
453 if (state
& DMACSR_BUSEXC
)
456 scsierror("DMA failed");
461 sc
->dma_len
-= resid
;
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
));
469 /* scsierror("DMA not completed\n"); */