xref: /netbsd-src/sys/arch/mac68k/dev/mac68k5380.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: mac68k5380.c,v 1.40 2003/07/15 02:43:17 lukem Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Allen Briggs
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Allen Briggs
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Derived from atari5380.c for the mac68k port of NetBSD.
33  *
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: mac68k5380.c,v 1.40 2003/07/15 02:43:17 lukem Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/device.h>
43 #include <sys/syslog.h>
44 #include <sys/buf.h>
45 
46 #include <uvm/uvm_extern.h>
47 
48 #include <dev/scsipi/scsi_all.h>
49 #include <dev/scsipi/scsipi_all.h>
50 #include <dev/scsipi/scsi_message.h>
51 #include <dev/scsipi/scsiconf.h>
52 
53 /*
54  * Include the driver definitions
55  */
56 #include "ncr5380reg.h"
57 
58 #include <machine/cpu.h>
59 #include <machine/stdarg.h>
60 #include <machine/viareg.h>
61 
62 #include <mac68k/dev/ncr5380var.h>
63 
64 /*
65  * Set the various driver options
66  */
67 #define	NREQ		18	/* Size of issue queue			*/
68 #define	AUTO_SENSE	1	/* Automatically issue a request-sense 	*/
69 
70 #define	DRNAME		ncrscsi	/* used in various prints	*/
71 #undef	DBG_SEL			/* Show the selection process		*/
72 #undef	DBG_REQ			/* Show enqueued/ready requests		*/
73 #undef	DBG_NOWRITE		/* Do not allow writes to the targets	*/
74 #undef	DBG_PIO			/* Show the polled-I/O process		*/
75 #undef	DBG_INF			/* Show information transfer process	*/
76 #define	DBG_NOSTATIC		/* No static functions, all in DDB trace*/
77 #define	DBG_PID		25	/* Keep track of driver			*/
78 #ifdef DBG_NOSTATIC
79 #	define	static
80 #endif
81 #ifdef DBG_SEL
82 #	define	DBG_SELPRINT(a,b)	printf(a,b)
83 #else
84 #	define DBG_SELPRINT(a,b)
85 #endif
86 #ifdef DBG_PIO
87 #	define DBG_PIOPRINT(a,b,c) 	printf(a,b,c)
88 #else
89 #	define DBG_PIOPRINT(a,b,c)
90 #endif
91 #ifdef DBG_INF
92 #	define DBG_INFPRINT(a,b,c)	a(b,c)
93 #else
94 #	define DBG_INFPRINT(a,b,c)
95 #endif
96 #ifdef DBG_PID
97 	/* static	char	*last_hit = NULL, *olast_hit = NULL; */
98 	static char *last_hit[DBG_PID];
99 #	define	PID(a)	\
100 	{ int i; \
101 	  for (i=0; i< DBG_PID-1; i++) \
102 		last_hit[i] = last_hit[i+1]; \
103 	  last_hit[DBG_PID-1] = a; }
104 #else
105 #	define	PID(a)
106 #endif
107 
108 #undef 	REAL_DMA		/* Use DMA if sensible			*/
109 #define scsi_ipending()		(GET_5380_REG(NCR5380_DMSTAT) & SC_IRQ_SET)
110 #define fair_to_keep_dma()	1
111 #define claimed_dma()		1
112 #define reconsider_dma()
113 #define	USE_PDMA	1	/* Use special pdma-transfer function	*/
114 #define MIN_PHYS	0x2000	/* pdma space w/ /DSACK is only 0x2000  */
115 
116 #define	ENABLE_NCR5380(sc)	cur_softc = sc;
117 
118 /*
119  * softc of currently active controller (well, we only have one for now).
120  */
121 
122 static struct ncr_softc	*cur_softc;
123 
124 struct scsi_5380 {
125 	volatile u_char	scsi_5380[8*16]; /* 8 regs, 1 every 16th byte. */
126 };
127 
128 extern vaddr_t		SCSIBase;
129 static volatile u_char	*ncr		= (volatile u_char *) 0x10000;
130 static volatile u_char	*ncr_5380_with_drq	= (volatile u_char *)  0x6000;
131 static volatile u_char	*ncr_5380_without_drq	= (volatile u_char *) 0x12000;
132 
133 #define SCSI_5380		((struct scsi_5380 *) ncr)
134 #define GET_5380_REG(rnum)	SCSI_5380->scsi_5380[((rnum)<<4)]
135 #define SET_5380_REG(rnum,val)	(SCSI_5380->scsi_5380[((rnum)<<4)] = (val))
136 
137 static void	ncr5380_irq_intr(void *);
138 static void	ncr5380_drq_intr(void *);
139 static void	do_ncr5380_drq_intr __P((void *));
140 
141 static __inline__ void	scsi_clr_ipend __P((void));
142 static		  void	scsi_mach_init __P((struct ncr_softc *sc));
143 static		  int	machine_match __P((struct device *parent,
144 			    struct cfdata *cf, void *aux,
145 			    struct cfdriver *cd));
146 static __inline__ int	pdma_ready __P((void));
147 static		  int	transfer_pdma __P((u_char *phasep, u_char *data,
148 					u_long *count));
149 
150 static __inline__ void
151 scsi_clr_ipend()
152 {
153 	int	tmp;
154 
155 	tmp = GET_5380_REG(NCR5380_IRCV);
156 	scsi_clear_irq();
157 }
158 
159 static void
160 scsi_mach_init(sc)
161 	struct ncr_softc	*sc;
162 {
163 	static int	initted = 0;
164 
165 	if (initted++)
166 		panic("scsi_mach_init called again.");
167 
168 	ncr		= (volatile u_char *)
169 			  (SCSIBase + (u_long) ncr);
170 	ncr_5380_with_drq	= (volatile u_char *)
171 			  (SCSIBase + (u_int) ncr_5380_with_drq);
172 	ncr_5380_without_drq	= (volatile u_char *)
173 			  (SCSIBase + (u_int) ncr_5380_without_drq);
174 
175 	if (VIA2 == VIA2OFF) {
176 		scsi_enable = Via1Base + VIA2 * 0x2000 + vIER;
177 		scsi_flag   = Via1Base + VIA2 * 0x2000 + vIFR;
178 	} else {
179 		scsi_enable = Via1Base + VIA2 * 0x2000 + rIER;
180 		scsi_flag   = Via1Base + VIA2 * 0x2000 + rIFR;
181 	}
182 
183 	via2_register_irq(VIA2_SCSIIRQ, ncr5380_irq_intr, sc);
184 	via2_register_irq(VIA2_SCSIDRQ, ncr5380_drq_intr, sc);
185 }
186 
187 static int
188 machine_match(parent, cf, aux, cd)
189 	struct device *parent;
190 	struct cfdata *cf;
191 	void *aux;
192 	struct cfdriver *cd;
193 {
194 	if (!mac68k_machine.scsi80)
195 		return 0;
196 	return 1;
197 }
198 
199 #if USE_PDMA
200 int	pdma_5380_dir = 0;
201 
202 u_char	*pending_5380_data;
203 u_long	pending_5380_count;
204 
205 #define NCR5380_PDMA_DEBUG 1 	/* Maybe we try with this off eventually. */
206 
207 #if NCR5380_PDMA_DEBUG
208 int		pdma_5380_sends = 0;
209 int		pdma_5380_bytes = 0;
210 
211 void
212 pdma_stat()
213 {
214 	printf("PDMA SCSI: %d xfers completed for %d bytes.\n",
215 		pdma_5380_sends, pdma_5380_bytes);
216 	printf("pdma_5380_dir = %d\t",
217 		pdma_5380_dir);
218 	printf("datap = %p, remainder = %ld.\n",
219 		pending_5380_data, pending_5380_count);
220 	scsi_show();
221 }
222 #endif
223 
224 void
225 pdma_cleanup(void)
226 {
227 	SC_REQ	*reqp = connected;
228 	int	s;
229 
230 	s = splbio();
231 	PID("pdma_cleanup0");
232 
233 	pdma_5380_dir = 0;
234 
235 #if NCR5380_PDMA_DEBUG
236 	pdma_5380_sends++;
237 	pdma_5380_bytes+=(reqp->xdata_len - pending_5380_count);
238 #endif
239 
240 	/*
241 	 * Update pointers.
242 	 */
243 	reqp->xdata_ptr += reqp->xdata_len - pending_5380_count;
244 	reqp->xdata_len  = pending_5380_count;
245 
246 	/*
247 	 * Reset DMA mode.
248 	 */
249 	SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE) & ~SC_M_DMA);
250 
251 	/*
252 	 * Clear any pending interrupts.
253 	 */
254 	scsi_clr_ipend();
255 
256 	/*
257 	 * Tell interrupt functions that DMA has ended.
258 	 */
259 	reqp->dr_flag &= ~DRIVER_IN_DMA;
260 
261 	SET_5380_REG(NCR5380_MODE, IMODE_BASE);
262 	SET_5380_REG(NCR5380_ICOM, 0);
263 
264 	splx(s);
265 
266 	/*
267 	 * Back for more punishment.
268 	 */
269 	PID("pdma_cleanup1");
270 	run_main(cur_softc);
271 	PID("pdma_cleanup2");
272 }
273 #endif
274 
275 static __inline__ int
276 pdma_ready()
277 {
278 #if USE_PDMA
279 	SC_REQ	*reqp = connected;
280 	int	dmstat, idstat;
281 extern	u_char	ncr5380_no_parchk;
282 
283 	PID("pdma_ready0");
284 	if (pdma_5380_dir) {
285 		PID("pdma_ready1.");
286 		/*
287 		 * For a phase mis-match, ATN is a "don't care," IRQ is 1 and
288 		 * all other bits in the Bus & Status Register are 0.  Also,
289 		 * the current SCSI Bus Status Register has a 1 for BSY and
290 		 * REQ.  Since we're just checking that this interrupt isn't a
291 		 * reselection or a reset, we just check for either.
292 		 */
293 		dmstat = GET_5380_REG(NCR5380_DMSTAT);
294 		idstat = GET_5380_REG(NCR5380_IDSTAT);
295 		if (   ((dmstat & (0xff & ~SC_ATN_STAT)) == SC_IRQ_SET)
296 		    && ((idstat & (SC_S_BSY|SC_S_REQ))
297 			== (SC_S_BSY | SC_S_REQ)) ) {
298 			PID("pdma_ready2");
299 			pdma_cleanup();
300 			return 1;
301 		} else if (PH_IN(reqp->phase) && (dmstat & SC_PAR_ERR)) {
302 			if (!(ncr5380_no_parchk & (1 << reqp->targ_id)))
303 				/* XXX: Should be parity error ???? */
304 				reqp->xs->error = XS_DRIVER_STUFFUP;
305 			PID("pdma_ready3");
306 			/* XXX: is this the right reaction? */
307 			pdma_cleanup();
308 			return 1;
309 		} else if (   !(idstat & SC_S_REQ)
310 			   || (((idstat>>2) & 7) != reqp->phase)) {
311 #ifdef DIAGNOSTIC
312 			/* XXX: is this the right reaction? Can this happen? */
313 			scsi_show();
314 			printf("Unexpected phase change.\n");
315 #endif
316 			reqp->xs->error = XS_DRIVER_STUFFUP;
317 			pdma_cleanup();
318 			return 1;
319 		} else {
320 			scsi_show();
321 			panic("Spurious interrupt during PDMA xfer.");
322 		}
323 	} else
324 		PID("pdma_ready4");
325 #endif
326 	return 0;
327 }
328 
329 static void
330 ncr5380_irq_intr(p)
331 	void	*p;
332 {
333 	PID("irq");
334 
335 #if USE_PDMA
336 	if (pdma_ready()) {
337 		return;
338 	}
339 #endif
340 	scsi_idisable();
341 	ncr_ctrl_intr(cur_softc);
342 }
343 
344 /*
345  * This is the meat of the PDMA transfer.
346  * When we get here, we shove data as fast as the mac can take it.
347  * We depend on several things:
348  *   * All macs after the Mac Plus that have a 5380 chip should have a general
349  *     logic IC that handshakes data for blind transfers.
350  *   * If the SCSI controller finishes sending/receiving data before we do,
351  *     the same general logic IC will generate a /BERR for us in short order.
352  *   * The fault address for said /BERR minus the base address for the
353  *     transfer will be the amount of data that was actually written.
354  *
355  * We use the nofault flag and the setjmp/longjmp in locore.s so we can
356  * detect and handle the bus error for early termination of a command.
357  * This is usually caused by a disconnecting target.
358  */
359 static void
360 do_ncr5380_drq_intr(p)
361 	void	*p;
362 {
363 #if USE_PDMA
364 extern	int			*nofault, m68k_fault_addr;
365 	label_t			faultbuf;
366 	register int		count;
367 	volatile u_int32_t	*long_drq;
368 	u_int32_t		*long_data;
369 	volatile u_int8_t	*drq, tmp_data;
370 	u_int8_t		*data;
371 
372 #if DBG_PID
373 	if (pdma_5380_dir == 2) {
374 		PID("drq (in)");
375 	} else {
376 		PID("drq (out)");
377 	}
378 #endif
379 
380 	/*
381 	 * Setup for a possible bus error caused by SCSI controller
382 	 * switching out of DATA-IN/OUT before we're done with the
383 	 * current transfer.
384 	 */
385 	nofault = (int *) &faultbuf;
386 
387 	if (setjmp((label_t *) nofault)) {
388 		PID("drq berr");
389 		nofault = (int *) 0;
390 		count = (  (u_long) m68k_fault_addr
391 			 - (u_long) ncr_5380_with_drq);
392 		if ((count < 0) || (count > pending_5380_count)) {
393 			printf("pdma %s: cnt = %d (0x%x) (pending cnt %ld)\n",
394 				(pdma_5380_dir == 2) ? "in" : "out",
395 				count, count, pending_5380_count);
396 			panic("something is wrong");
397 		}
398 
399 		pending_5380_data += count;
400 		pending_5380_count -= count;
401 
402 		m68k_fault_addr = 0;
403 
404 		PID("end drq early");
405 
406 		return;
407 	}
408 
409 	if (pdma_5380_dir == 2) { /* Data In */
410 		int	resid;
411 
412 		/*
413 		 * Get the dest address aligned.
414 		 */
415 		resid = count = min(pending_5380_count,
416 				    4 - (((int) pending_5380_data) & 0x3));
417 		if (count && (count < 4)) {
418 			data = (u_int8_t *) pending_5380_data;
419 			drq = (u_int8_t *) ncr_5380_with_drq;
420 			while (count) {
421 #define R1	*data++ = *drq++
422 				R1; count--;
423 #undef R1
424 			}
425 			pending_5380_data += resid;
426 			pending_5380_count -= resid;
427 		}
428 
429 		/*
430 		 * Get ready to start the transfer.
431 		 */
432 		while (pending_5380_count) {
433 		int dcount;
434 
435 		dcount = count = min(pending_5380_count, MIN_PHYS);
436 		long_drq = (volatile u_int32_t *) ncr_5380_with_drq;
437 		long_data = (u_int32_t *) pending_5380_data;
438 
439 #define R4	*long_data++ = *long_drq++
440 		while ( count > 64 ) {
441 			R4; R4; R4; R4; R4; R4; R4; R4;
442 			R4; R4; R4; R4; R4; R4; R4; R4;	/* 64 */
443 			count -= 64;
444 		}
445 		while (count > 8) {
446 			R4; R4; count -= 8;
447 		}
448 #undef R4
449 		data = (u_int8_t *) long_data;
450 		drq = (u_int8_t *) long_drq;
451 		while (count) {
452 #define R1	*data++ = *drq++
453 			R1; count--;
454 #undef R1
455 		}
456 		pending_5380_count -= dcount;
457 		pending_5380_data += dcount;
458 		}
459 	} else {
460 		int	resid;
461 
462 		/*
463 		 * Get the source address aligned.
464 		 */
465 		resid = count = min(pending_5380_count,
466 				    4 - (((int) pending_5380_data) & 0x3));
467 		if (count && (count < 4)) {
468 			data = (u_int8_t *) pending_5380_data;
469 			drq = (u_int8_t *) ncr_5380_with_drq;
470 			while (count) {
471 #define W1	*drq++ = *data++
472 				W1; count--;
473 #undef W1
474 			}
475 			pending_5380_data += resid;
476 			pending_5380_count -= resid;
477 		}
478 
479 		/*
480 		 * Get ready to start the transfer.
481 		 */
482 		while (pending_5380_count) {
483 		int dcount;
484 
485 		dcount = count = min(pending_5380_count, MIN_PHYS);
486 		long_drq = (volatile u_int32_t *) ncr_5380_with_drq;
487 		long_data = (u_int32_t *) pending_5380_data;
488 
489 #define W4	*long_drq++ = *long_data++
490 		while ( count > 64 ) {
491 			W4; W4; W4; W4; W4; W4; W4; W4;
492 			W4; W4; W4; W4; W4; W4; W4; W4; /*  64 */
493 			count -= 64;
494 		}
495 		while ( count > 8 ) {
496 			W4; W4;
497 			count -= 8;
498 		}
499 #undef W4
500 		data = (u_int8_t *) long_data;
501 		drq = (u_int8_t *) long_drq;
502 		while (count) {
503 #define W1	*drq++ = *data++
504 			W1; count--;
505 #undef W1
506 		}
507 		pending_5380_count -= dcount;
508 		pending_5380_data += dcount;
509 		}
510 
511 		PID("write complete");
512 
513 		drq = (volatile u_int8_t *) ncr_5380_with_drq;
514 		tmp_data = *drq;
515 
516 		PID("read a byte to force a phase change");
517 	}
518 
519 	/*
520 	 * OK.  No bus error occurred above.  Clear the nofault flag
521 	 * so we no longer short-circuit bus errors.
522 	 */
523 	nofault = (int *) 0;
524 
525 	PID("end drq");
526 	return;
527 #else
528 	return;
529 #endif	/* if USE_PDMA */
530 }
531 
532 static void
533 ncr5380_drq_intr(p)
534 	void	*p;
535 {
536 	while (GET_5380_REG(NCR5380_DMSTAT) & SC_DMA_REQ) {
537 		do_ncr5380_drq_intr(p);
538 		scsi_clear_drq();
539 	}
540 }
541 
542 #if USE_PDMA
543 
544 #define SCSI_TIMEOUT_VAL	10000000
545 
546 static int
547 transfer_pdma(phasep, data, count)
548 	u_char	*phasep;
549 	u_char	*data;
550 	u_long	*count;
551 {
552 	SC_REQ	*reqp = connected;
553 	int	len = *count, s, scsi_timeout = SCSI_TIMEOUT_VAL;
554 
555 	if (pdma_5380_dir) {
556 		panic("ncrscsi: transfer_pdma called when operation already "
557 			"pending.");
558 	}
559 	PID("transfer_pdma0")
560 
561 	/*
562  	 * Don't bother with PDMA if we can't sleep or for small transfers.
563  	 */
564 	if (reqp->dr_flag & DRIVER_NOINT) {
565 		PID("pdma, falling back to transfer_pio.")
566 		transfer_pio(phasep, data, count, 0);
567 		return -1;
568 	}
569 
570 	/*
571 	 * We are probably already at spl2(), so this is likely a no-op.
572 	 * Paranoia.
573 	 */
574 	s = splbio();
575 
576 	scsi_idisable();
577 
578 	/*
579 	 * Match phases with target.
580 	 */
581 	SET_5380_REG(NCR5380_TCOM, *phasep);
582 
583 	/*
584 	 * Clear pending interrupts.
585 	 */
586 	scsi_clr_ipend();
587 
588 	/*
589 	 * Wait until target asserts BSY.
590 	 */
591 	while (    ((GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY) == 0)
592 		&& (--scsi_timeout) );
593 	if (!scsi_timeout) {
594 #if DIAGNOSTIC
595 		printf("scsi timeout: waiting for BSY in %s.\n",
596 			(*phasep == PH_DATAOUT) ? "pdma_out" : "pdma_in");
597 #endif
598 		goto scsi_timeout_error;
599 	}
600 
601 	/*
602 	 * Tell the driver that we're in DMA mode.
603 	 */
604 	reqp->dr_flag |= DRIVER_IN_DMA;
605 
606 	/*
607 	 * Load transfer values for DRQ interrupt handlers.
608 	 */
609 	pending_5380_data = data;
610 	pending_5380_count = len;
611 
612 	/*
613 	 * Set the transfer function to be called on DRQ interrupts.
614 	 * And note that we're waiting.
615 	 */
616 	switch (*phasep) {
617 	default:
618 		panic("Unexpected phase in transfer_pdma.");
619 	case PH_DATAOUT:
620 		pdma_5380_dir = 1;
621 		SET_5380_REG(NCR5380_ICOM, GET_5380_REG(NCR5380_ICOM)|SC_ADTB);
622 		SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE)|SC_M_DMA);
623 		SET_5380_REG(NCR5380_DMSTAT, 0);
624 		break;
625 	case PH_DATAIN:
626 		pdma_5380_dir = 2;
627 		SET_5380_REG(NCR5380_ICOM, 0);
628 		SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE)|SC_M_DMA);
629 		SET_5380_REG(NCR5380_IRCV, 0);
630 		break;
631 	}
632 
633 	PID("waiting for interrupt.")
634 
635 	/*
636 	 * Now that we're set up, enable interrupts and drop processor
637 	 * priority back down.
638 	 */
639 	scsi_ienable();
640 	splx(s);
641 	return 0;
642 
643 scsi_timeout_error:
644 	/*
645 	 * Clear the DMA mode.
646 	 */
647 	SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE) & ~SC_M_DMA);
648 	return -1;
649 }
650 #endif /* if USE_PDMA */
651 
652 /* Include general routines. */
653 #include <mac68k/dev/ncr5380.c>
654