xref: /netbsd-src/sys/arch/mac68k/dev/mac68k5380.c (revision d1579b2d70337e1b895f03478838f880e450f6da)
1*d1579b2dSriastradh /*	$NetBSD: mac68k5380.c,v 1.50 2018/09/03 16:29:25 riastradh Exp $	*/
2e46e8e7aSbriggs 
3e46e8e7aSbriggs /*
4e46e8e7aSbriggs  * Copyright (c) 1995 Allen Briggs
5e46e8e7aSbriggs  * All rights reserved.
6e46e8e7aSbriggs  *
7e46e8e7aSbriggs  * Redistribution and use in source and binary forms, with or without
8e46e8e7aSbriggs  * modification, are permitted provided that the following conditions
9e46e8e7aSbriggs  * are met:
10e46e8e7aSbriggs  * 1. Redistributions of source code must retain the above copyright
11e46e8e7aSbriggs  *    notice, this list of conditions and the following disclaimer.
12e46e8e7aSbriggs  * 2. Redistributions in binary form must reproduce the above copyright
13e46e8e7aSbriggs  *    notice, this list of conditions and the following disclaimer in the
14e46e8e7aSbriggs  *    documentation and/or other materials provided with the distribution.
15e46e8e7aSbriggs  * 3. All advertising materials mentioning features or use of this software
16e46e8e7aSbriggs  *    must display the following acknowledgement:
17e46e8e7aSbriggs  *      This product includes software developed by Allen Briggs
18e46e8e7aSbriggs  * 4. The name of the author may not be used to endorse or promote products
19e46e8e7aSbriggs  *    derived from this software without specific prior written permission
20e46e8e7aSbriggs  *
21e46e8e7aSbriggs  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22e46e8e7aSbriggs  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23e46e8e7aSbriggs  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24e46e8e7aSbriggs  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25e46e8e7aSbriggs  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26e46e8e7aSbriggs  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27e46e8e7aSbriggs  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28e46e8e7aSbriggs  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29e46e8e7aSbriggs  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30e46e8e7aSbriggs  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31e46e8e7aSbriggs  *
32e46e8e7aSbriggs  * Derived from atari5380.c for the mac68k port of NetBSD.
33e46e8e7aSbriggs  *
34e46e8e7aSbriggs  */
35e46e8e7aSbriggs 
364b2744bfSlukem #include <sys/cdefs.h>
37*d1579b2dSriastradh __KERNEL_RCSID(0, "$NetBSD: mac68k5380.c,v 1.50 2018/09/03 16:29:25 riastradh Exp $");
384b2744bfSlukem 
39e46e8e7aSbriggs #include <sys/param.h>
40e46e8e7aSbriggs #include <sys/systm.h>
41e46e8e7aSbriggs #include <sys/kernel.h>
42e46e8e7aSbriggs #include <sys/device.h>
43e46e8e7aSbriggs #include <sys/syslog.h>
44e46e8e7aSbriggs #include <sys/buf.h>
45d57736a7Sthorpej 
46d57736a7Sthorpej #include <uvm/uvm_extern.h>
47d57736a7Sthorpej 
486f3bab1fSbouyer #include <dev/scsipi/scsi_all.h>
496f3bab1fSbouyer #include <dev/scsipi/scsipi_all.h>
506f3bab1fSbouyer #include <dev/scsipi/scsi_message.h>
516f3bab1fSbouyer #include <dev/scsipi/scsiconf.h>
52e46e8e7aSbriggs 
53e46e8e7aSbriggs /*
54e46e8e7aSbriggs  * Include the driver definitions
55e46e8e7aSbriggs  */
5637163421Sbriggs #include "ncr5380reg.h"
57e46e8e7aSbriggs 
588a007407Sscottr #include <machine/cpu.h>
5908126b5bSbriggs #include <machine/viareg.h>
60e46e8e7aSbriggs 
61c988c6f8Sscottr #include <mac68k/dev/ncr5380var.h>
6237163421Sbriggs 
63e46e8e7aSbriggs /*
64e46e8e7aSbriggs  * Set the various driver options
65e46e8e7aSbriggs  */
66e46e8e7aSbriggs #define	NREQ		18	/* Size of issue queue			*/
67e46e8e7aSbriggs #define	AUTO_SENSE	1	/* Automatically issue a request-sense 	*/
68e46e8e7aSbriggs 
69e46e8e7aSbriggs #define	DRNAME		ncrscsi	/* used in various prints	*/
70e46e8e7aSbriggs #undef	DBG_SEL			/* Show the selection process		*/
71e46e8e7aSbriggs #undef	DBG_REQ			/* Show enqueued/ready requests		*/
72e46e8e7aSbriggs #undef	DBG_NOWRITE		/* Do not allow writes to the targets	*/
73e46e8e7aSbriggs #undef	DBG_PIO			/* Show the polled-I/O process		*/
74e46e8e7aSbriggs #undef	DBG_INF			/* Show information transfer process	*/
75e46e8e7aSbriggs #define	DBG_NOSTATIC		/* No static functions, all in DDB trace*/
762a878c9aSbriggs #define	DBG_PID		25	/* Keep track of driver			*/
772a878c9aSbriggs #ifdef DBG_NOSTATIC
782a878c9aSbriggs #	define	static
792a878c9aSbriggs #endif
802a878c9aSbriggs #ifdef DBG_SEL
8140ecbf8eSchristos #	define	DBG_SELPRINT(a,b)	printf(a,b)
822a878c9aSbriggs #else
832a878c9aSbriggs #	define DBG_SELPRINT(a,b)
842a878c9aSbriggs #endif
852a878c9aSbriggs #ifdef DBG_PIO
8640ecbf8eSchristos #	define DBG_PIOPRINT(a,b,c) 	printf(a,b,c)
872a878c9aSbriggs #else
882a878c9aSbriggs #	define DBG_PIOPRINT(a,b,c)
892a878c9aSbriggs #endif
902a878c9aSbriggs #ifdef DBG_INF
912a878c9aSbriggs #	define DBG_INFPRINT(a,b,c)	a(b,c)
922a878c9aSbriggs #else
932a878c9aSbriggs #	define DBG_INFPRINT(a,b,c)
942a878c9aSbriggs #endif
952a878c9aSbriggs #ifdef DBG_PID
962a878c9aSbriggs 	/* static	char	*last_hit = NULL, *olast_hit = NULL; */
9761f16670Sjmc 	static const char *last_hit[DBG_PID];
982a878c9aSbriggs #	define	PID(a)	\
992a878c9aSbriggs 	{ int i; \
1002a878c9aSbriggs 	  for (i = 0; i < DBG_PID - 1; i++) \
1012a878c9aSbriggs 		last_hit[i] = last_hit[i + 1]; \
1022a878c9aSbriggs 	  last_hit[DBG_PID - 1] = a; }
1032a878c9aSbriggs #else
1042a878c9aSbriggs #	define	PID(a)
1052a878c9aSbriggs #endif
1062a878c9aSbriggs 
107e46e8e7aSbriggs #undef 	REAL_DMA		/* Use DMA if sensible			*/
108ab3f7419Sbriggs #define scsi_ipending()		(GET_5380_REG(NCR5380_DMSTAT) & SC_IRQ_SET)
109e46e8e7aSbriggs #define fair_to_keep_dma()	1
110e46e8e7aSbriggs #define claimed_dma()		1
111e46e8e7aSbriggs #define reconsider_dma()
112e46e8e7aSbriggs #define	USE_PDMA	1	/* Use special pdma-transfer function	*/
113e23598f2Sbriggs #define MIN_PHYS	0x2000	/* pdma space w/ /DSACK is only 0x2000  */
114e46e8e7aSbriggs 
115e46e8e7aSbriggs #define	ENABLE_NCR5380(sc)	cur_softc = sc;
116e46e8e7aSbriggs 
117e46e8e7aSbriggs /*
118e46e8e7aSbriggs  * softc of currently active controller (well, we only have one for now).
119e46e8e7aSbriggs  */
120e46e8e7aSbriggs 
121e46e8e7aSbriggs static struct ncr_softc	*cur_softc;
122e46e8e7aSbriggs 
123e46e8e7aSbriggs struct scsi_5380 {
124e46e8e7aSbriggs 	volatile u_char	scsi_5380[8*16]; /* 8 regs, 1 every 16th byte. */
125e46e8e7aSbriggs };
126e46e8e7aSbriggs 
12784af6365Sscottr extern vaddr_t		SCSIBase;
128e46e8e7aSbriggs static volatile u_char	*ncr		= (volatile u_char *) 0x10000;
129e46e8e7aSbriggs static volatile u_char	*ncr_5380_with_drq	= (volatile u_char *)  0x6000;
130e46e8e7aSbriggs static volatile u_char	*ncr_5380_without_drq	= (volatile u_char *) 0x12000;
131e46e8e7aSbriggs 
13261f16670Sjmc #define SCSI_5380		((volatile struct scsi_5380 *) ncr)
133e46e8e7aSbriggs #define GET_5380_REG(rnum)	SCSI_5380->scsi_5380[((rnum)<<4)]
134e46e8e7aSbriggs #define SET_5380_REG(rnum,val)	(SCSI_5380->scsi_5380[((rnum)<<4)] = (val))
135e46e8e7aSbriggs 
136014b4041Sbriggs static void	ncr5380_irq_intr(void *);
137014b4041Sbriggs static void	ncr5380_drq_intr(void *);
1387acd68b1Schs static void	do_ncr5380_drq_intr(void *);
139a7783604Sbriggs 
140a783fa47Smrg static void	scsi_clr_ipend(void);
1417acd68b1Schs static void	scsi_mach_init(struct ncr_softc *);
142cbab9cadSchs static int	machine_match(device_t, cfdata_t, void *,
1437acd68b1Schs 			      struct cfdriver *);
144a783fa47Smrg static int	pdma_ready(void);
1457acd68b1Schs static int	transfer_pdma(u_char *, u_char *, u_long *);
14637163421Sbriggs 
147a783fa47Smrg static void
scsi_clr_ipend(void)1487acd68b1Schs scsi_clr_ipend(void)
149e46e8e7aSbriggs {
150e46e8e7aSbriggs 
151652316b5Smartin 	GET_5380_REG(NCR5380_IRCV);
152014b4041Sbriggs 	scsi_clear_irq();
153e46e8e7aSbriggs }
154e46e8e7aSbriggs 
155e46e8e7aSbriggs static void
scsi_mach_init(struct ncr_softc * sc)1567acd68b1Schs scsi_mach_init(struct ncr_softc *sc)
157e46e8e7aSbriggs {
158e46e8e7aSbriggs 	static int initted = 0;
159e46e8e7aSbriggs 
160e46e8e7aSbriggs 	if (initted++)
1610f09ed48Sprovos 		panic("scsi_mach_init called again.");
162e46e8e7aSbriggs 
163e46e8e7aSbriggs 	ncr		= (volatile u_char *)
164e46e8e7aSbriggs 			  (SCSIBase + (u_long) ncr);
165e46e8e7aSbriggs 	ncr_5380_with_drq	= (volatile u_char *)
166e46e8e7aSbriggs 			  (SCSIBase + (u_int) ncr_5380_with_drq);
167e46e8e7aSbriggs 	ncr_5380_without_drq	= (volatile u_char *)
168e46e8e7aSbriggs 			  (SCSIBase + (u_int) ncr_5380_without_drq);
169a7783604Sbriggs 
170014b4041Sbriggs 	if (VIA2 == VIA2OFF) {
171a7783604Sbriggs 		scsi_enable = Via1Base + VIA2 * 0x2000 + vIER;
172014b4041Sbriggs 		scsi_flag   = Via1Base + VIA2 * 0x2000 + vIFR;
173014b4041Sbriggs 	} else {
174a7783604Sbriggs 		scsi_enable = Via1Base + VIA2 * 0x2000 + rIER;
175014b4041Sbriggs 		scsi_flag   = Via1Base + VIA2 * 0x2000 + rIFR;
176014b4041Sbriggs 	}
177a7783604Sbriggs 
178215c0b89Sscottr 	via2_register_irq(VIA2_SCSIIRQ, ncr5380_irq_intr, sc);
179215c0b89Sscottr 	via2_register_irq(VIA2_SCSIDRQ, ncr5380_drq_intr, sc);
180e46e8e7aSbriggs }
181e46e8e7aSbriggs 
182e46e8e7aSbriggs static int
machine_match(device_t parent,cfdata_t cf,void * aux,struct cfdriver * cd)183cbab9cadSchs machine_match(device_t parent, cfdata_t cf, void *aux,
1847acd68b1Schs 	      struct cfdriver *cd)
185e46e8e7aSbriggs {
186e46e8e7aSbriggs 	if (!mac68k_machine.scsi80)
187e46e8e7aSbriggs 		return 0;
188e46e8e7aSbriggs 	return 1;
189e46e8e7aSbriggs }
190e46e8e7aSbriggs 
191e46e8e7aSbriggs #if USE_PDMA
192e46e8e7aSbriggs int	pdma_5380_dir = 0;
193e46e8e7aSbriggs 
194a7783604Sbriggs u_char	*pending_5380_data;
195a7783604Sbriggs u_long	pending_5380_count;
196e46e8e7aSbriggs 
197229c36d3Sbriggs #define NCR5380_PDMA_DEBUG 1 	/* Maybe we try with this off eventually. */
198e23598f2Sbriggs 
199229c36d3Sbriggs #if NCR5380_PDMA_DEBUG
200e46e8e7aSbriggs int		pdma_5380_sends = 0;
201bee422d9Sbriggs int		pdma_5380_bytes = 0;
202e46e8e7aSbriggs 
203e46e8e7aSbriggs void
pdma_stat(void)2047acd68b1Schs pdma_stat(void)
205e46e8e7aSbriggs {
20640ecbf8eSchristos 	printf("PDMA SCSI: %d xfers completed for %d bytes.\n",
207a7783604Sbriggs 		pdma_5380_sends, pdma_5380_bytes);
20840ecbf8eSchristos 	printf("pdma_5380_dir = %d\t",
209a7783604Sbriggs 		pdma_5380_dir);
21040ecbf8eSchristos 	printf("datap = %p, remainder = %ld.\n",
211e46e8e7aSbriggs 		pending_5380_data, pending_5380_count);
212229c36d3Sbriggs 	scsi_show();
213e46e8e7aSbriggs }
214e46e8e7aSbriggs #endif
215e46e8e7aSbriggs 
216e46e8e7aSbriggs void
pdma_cleanup(void)217bee422d9Sbriggs pdma_cleanup(void)
218bee422d9Sbriggs {
219bee422d9Sbriggs 	SC_REQ	*reqp = connected;
22037163421Sbriggs 	int	s;
221bee422d9Sbriggs 
222bee422d9Sbriggs 	s = splbio();
2232a878c9aSbriggs 	PID("pdma_cleanup0");
224bee422d9Sbriggs 
225a7783604Sbriggs 	pdma_5380_dir = 0;
226bee422d9Sbriggs 
227229c36d3Sbriggs #if NCR5380_PDMA_DEBUG
228bee422d9Sbriggs 	pdma_5380_sends++;
229bee422d9Sbriggs 	pdma_5380_bytes+=(reqp->xdata_len - pending_5380_count);
230bee422d9Sbriggs #endif
231bee422d9Sbriggs 
232bee422d9Sbriggs 	/*
233bee422d9Sbriggs 	 * Update pointers.
234bee422d9Sbriggs 	 */
235bee422d9Sbriggs 	reqp->xdata_ptr += reqp->xdata_len - pending_5380_count;
236bee422d9Sbriggs 	reqp->xdata_len  = pending_5380_count;
237bee422d9Sbriggs 
238bee422d9Sbriggs 	/*
239bee422d9Sbriggs 	 * Reset DMA mode.
240bee422d9Sbriggs 	 */
241bee422d9Sbriggs 	SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE) & ~SC_M_DMA);
242bee422d9Sbriggs 
243bee422d9Sbriggs 	/*
244e23598f2Sbriggs 	 * Clear any pending interrupts.
245e23598f2Sbriggs 	 */
246e23598f2Sbriggs 	scsi_clr_ipend();
247e23598f2Sbriggs 
248e23598f2Sbriggs 	/*
249bee422d9Sbriggs 	 * Tell interrupt functions that DMA has ended.
250bee422d9Sbriggs 	 */
251bee422d9Sbriggs 	reqp->dr_flag &= ~DRIVER_IN_DMA;
252bee422d9Sbriggs 
253bee422d9Sbriggs 	SET_5380_REG(NCR5380_MODE, IMODE_BASE);
254bee422d9Sbriggs 	SET_5380_REG(NCR5380_ICOM, 0);
255bee422d9Sbriggs 
256bee422d9Sbriggs 	splx(s);
257bee422d9Sbriggs 
258bee422d9Sbriggs 	/*
259bee422d9Sbriggs 	 * Back for more punishment.
260bee422d9Sbriggs 	 */
2612a878c9aSbriggs 	PID("pdma_cleanup1");
262bee422d9Sbriggs 	run_main(cur_softc);
2632a878c9aSbriggs 	PID("pdma_cleanup2");
264bee422d9Sbriggs }
26523216939Sbriggs #endif
266bee422d9Sbriggs 
267a783fa47Smrg static int
pdma_ready(void)2687acd68b1Schs pdma_ready(void)
269e46e8e7aSbriggs {
27023216939Sbriggs #if USE_PDMA
27123216939Sbriggs 	SC_REQ	*reqp = connected;
27223216939Sbriggs 	int	dmstat, idstat;
27323216939Sbriggs extern	u_char	ncr5380_no_parchk;
27423216939Sbriggs 
2752a878c9aSbriggs 	PID("pdma_ready0");
276a7783604Sbriggs 	if (pdma_5380_dir) {
2775c13e718Sbriggs 		PID("pdma_ready1.");
278e46e8e7aSbriggs 		/*
279e46e8e7aSbriggs 		 * For a phase mis-match, ATN is a "don't care," IRQ is 1 and
280e46e8e7aSbriggs 		 * all other bits in the Bus & Status Register are 0.  Also,
281e46e8e7aSbriggs 		 * the current SCSI Bus Status Register has a 1 for BSY and
282e46e8e7aSbriggs 		 * REQ.  Since we're just checking that this interrupt isn't a
283e46e8e7aSbriggs 		 * reselection or a reset, we just check for either.
284e46e8e7aSbriggs 		 */
285014b4041Sbriggs 		dmstat = GET_5380_REG(NCR5380_DMSTAT);
28623216939Sbriggs 		idstat = GET_5380_REG(NCR5380_IDSTAT);
28723216939Sbriggs 		if (   ((dmstat & (0xff & ~SC_ATN_STAT)) == SC_IRQ_SET)
28823216939Sbriggs 		    && ((idstat & (SC_S_BSY|SC_S_REQ))
28923216939Sbriggs 			== (SC_S_BSY | SC_S_REQ)) ) {
290014b4041Sbriggs 			PID("pdma_ready2");
29123216939Sbriggs 			pdma_cleanup();
29223216939Sbriggs 			return 1;
29323216939Sbriggs 		} else if (PH_IN(reqp->phase) && (dmstat & SC_PAR_ERR)) {
29423216939Sbriggs 			if (!(ncr5380_no_parchk & (1 << reqp->targ_id)))
29523216939Sbriggs 				/* XXX: Should be parity error ???? */
29623216939Sbriggs 				reqp->xs->error = XS_DRIVER_STUFFUP;
297014b4041Sbriggs 			PID("pdma_ready3");
29823216939Sbriggs 			/* XXX: is this the right reaction? */
29923216939Sbriggs 			pdma_cleanup();
30023216939Sbriggs 			return 1;
30123216939Sbriggs 		} else if (   !(idstat & SC_S_REQ)
30223216939Sbriggs 			   || (((idstat>>2) & 7) != reqp->phase)) {
30323216939Sbriggs #ifdef DIAGNOSTIC
30423216939Sbriggs 			/* XXX: is this the right reaction? Can this happen? */
30523216939Sbriggs 			scsi_show();
30640ecbf8eSchristos 			printf("Unexpected phase change.\n");
30723216939Sbriggs #endif
30823216939Sbriggs 			reqp->xs->error = XS_DRIVER_STUFFUP;
309bee422d9Sbriggs 			pdma_cleanup();
310e60752e5Sbriggs 			return 1;
311bee422d9Sbriggs 		} else {
312e46e8e7aSbriggs 			scsi_show();
3130f09ed48Sprovos 			panic("Spurious interrupt during PDMA xfer.");
314e46e8e7aSbriggs 		}
3152a878c9aSbriggs 	} else
316014b4041Sbriggs 		PID("pdma_ready4");
31723216939Sbriggs #endif
318e60752e5Sbriggs 	return 0;
319e60752e5Sbriggs }
320e60752e5Sbriggs 
321014b4041Sbriggs static void
ncr5380_irq_intr(void * p)3227acd68b1Schs ncr5380_irq_intr(void *p)
323e60752e5Sbriggs {
3242a878c9aSbriggs 	PID("irq");
325014b4041Sbriggs 
32623216939Sbriggs #if USE_PDMA
3274dc81200Sbriggs 	if (pdma_ready()) {
328e60752e5Sbriggs 		return;
329e60752e5Sbriggs 	}
33023216939Sbriggs #endif
331e46e8e7aSbriggs 	scsi_idisable();
332e46e8e7aSbriggs 	ncr_ctrl_intr(cur_softc);
333e46e8e7aSbriggs }
334e46e8e7aSbriggs 
335a7783604Sbriggs /*
336e23598f2Sbriggs  * This is the meat of the PDMA transfer.
337e23598f2Sbriggs  * When we get here, we shove data as fast as the mac can take it.
338e23598f2Sbriggs  * We depend on several things:
339e23598f2Sbriggs  *   * All macs after the Mac Plus that have a 5380 chip should have a general
340e23598f2Sbriggs  *     logic IC that handshakes data for blind transfers.
341e23598f2Sbriggs  *   * If the SCSI controller finishes sending/receiving data before we do,
342e23598f2Sbriggs  *     the same general logic IC will generate a /BERR for us in short order.
343e23598f2Sbriggs  *   * The fault address for said /BERR minus the base address for the
344e23598f2Sbriggs  *     transfer will be the amount of data that was actually written.
345e23598f2Sbriggs  *
346e23598f2Sbriggs  * We use the nofault flag and the setjmp/longjmp in locore.s so we can
347e23598f2Sbriggs  * detect and handle the bus error for early termination of a command.
348e23598f2Sbriggs  * This is usually caused by a disconnecting target.
349a7783604Sbriggs  */
350014b4041Sbriggs static void
do_ncr5380_drq_intr(void * p)3517acd68b1Schs do_ncr5380_drq_intr(void *p)
352e46e8e7aSbriggs {
353e46e8e7aSbriggs #if USE_PDMA
3540b7e64bbSscottr extern	int			*nofault, m68k_fault_addr;
355e23598f2Sbriggs 	label_t			faultbuf;
356e23598f2Sbriggs 	register int		count;
357e23598f2Sbriggs 	volatile u_int32_t	*long_drq;
358e23598f2Sbriggs 	u_int32_t		*long_data;
359652316b5Smartin 	volatile u_int8_t	*drq;
360e23598f2Sbriggs 	u_int8_t		*data;
361e23598f2Sbriggs 
3622a878c9aSbriggs #if DBG_PID
3632a878c9aSbriggs 	if (pdma_5380_dir == 2) {
3642a878c9aSbriggs 		PID("drq (in)");
3652a878c9aSbriggs 	} else {
3662a878c9aSbriggs 		PID("drq (out)");
3672a878c9aSbriggs 	}
368e46e8e7aSbriggs #endif
369e23598f2Sbriggs 
370e46e8e7aSbriggs 	/*
371e23598f2Sbriggs 	 * Setup for a possible bus error caused by SCSI controller
372e23598f2Sbriggs 	 * switching out of DATA-IN/OUT before we're done with the
373e23598f2Sbriggs 	 * current transfer.
374e46e8e7aSbriggs 	 */
375e23598f2Sbriggs 	nofault = (int *) &faultbuf;
376e23598f2Sbriggs 
377e23598f2Sbriggs 	if (setjmp((label_t *) nofault)) {
3782a878c9aSbriggs 		PID("drq berr");
379e23598f2Sbriggs 		nofault = (int *) 0;
3800b7e64bbSscottr 		count = (  (u_long) m68k_fault_addr
381e23598f2Sbriggs 			 - (u_long) ncr_5380_with_drq);
382e23598f2Sbriggs 		if ((count < 0) || (count > pending_5380_count)) {
38340ecbf8eSchristos 			printf("pdma %s: cnt = %d (0x%x) (pending cnt %ld)\n",
3849a1fb267Sbriggs 				(pdma_5380_dir == 2) ? "in" : "out",
3859a1fb267Sbriggs 				count, count, pending_5380_count);
386e23598f2Sbriggs 			panic("something is wrong");
387e23598f2Sbriggs 		}
388e23598f2Sbriggs 
389e23598f2Sbriggs 		pending_5380_data += count;
390e23598f2Sbriggs 		pending_5380_count -= count;
391e23598f2Sbriggs 
3920b7e64bbSscottr 		m68k_fault_addr = 0;
393014b4041Sbriggs 
394014b4041Sbriggs 		PID("end drq early");
395014b4041Sbriggs 
396bee422d9Sbriggs 		return;
397e46e8e7aSbriggs 	}
398e23598f2Sbriggs 
399e23598f2Sbriggs 	if (pdma_5380_dir == 2) { /* Data In */
400e23598f2Sbriggs 		int	resid;
401e23598f2Sbriggs 
402e23598f2Sbriggs 		/*
403e23598f2Sbriggs 		 * Get the dest address aligned.
404e23598f2Sbriggs 		 */
405*d1579b2dSriastradh 		resid = count = uimin(pending_5380_count,
406229c36d3Sbriggs 				    4 - (((int) pending_5380_data) & 0x3));
407229c36d3Sbriggs 		if (count && (count < 4)) {
408e23598f2Sbriggs 			data = (u_int8_t *) pending_5380_data;
40961f16670Sjmc 			drq = (volatile u_int8_t *) ncr_5380_with_drq;
410e23598f2Sbriggs 			while (count) {
4117456c67dShauke 				*data++ = *drq++;
4127456c67dShauke 				count--;
413e23598f2Sbriggs 			}
414e23598f2Sbriggs 			pending_5380_data += resid;
415e23598f2Sbriggs 			pending_5380_count -= resid;
416e23598f2Sbriggs 		}
417e23598f2Sbriggs 
418e23598f2Sbriggs 		/*
419e23598f2Sbriggs 		 * Get ready to start the transfer.
420e23598f2Sbriggs 		 */
42123216939Sbriggs 		while (pending_5380_count) {
42223216939Sbriggs 		int dcount;
42323216939Sbriggs 
424*d1579b2dSriastradh 		dcount = count = uimin(pending_5380_count, MIN_PHYS);
425e23598f2Sbriggs 		long_drq = (volatile u_int32_t *) ncr_5380_with_drq;
426c1138ce6Sbriggs 		long_data = (u_int32_t *) pending_5380_data;
427e23598f2Sbriggs 
428e23598f2Sbriggs #define R4	*long_data++ = *long_drq++
42916865d71Sbriggs 		while ( count > 64 ) {
430e23598f2Sbriggs 			R4; R4; R4; R4; R4; R4; R4; R4;
431e23598f2Sbriggs 			R4; R4; R4; R4; R4; R4; R4; R4;	/* 64 */
432014b4041Sbriggs 			count -= 64;
433e23598f2Sbriggs 		}
43416865d71Sbriggs 		while (count > 8) {
43516865d71Sbriggs 			R4; R4; count -= 8;
436e23598f2Sbriggs 		}
437e23598f2Sbriggs #undef R4
438e23598f2Sbriggs 		data = (u_int8_t *) long_data;
43961f16670Sjmc 		drq = (volatile u_int8_t *) long_drq;
440e23598f2Sbriggs 		while (count) {
4417456c67dShauke 			*data++ = *drq++;
4427456c67dShauke 			count--;
443e23598f2Sbriggs 		}
44423216939Sbriggs 		pending_5380_count -= dcount;
445c1138ce6Sbriggs 		pending_5380_data += dcount;
44623216939Sbriggs 		}
447e46e8e7aSbriggs 	} else {
448e23598f2Sbriggs 		int	resid;
449e23598f2Sbriggs 
450e23598f2Sbriggs 		/*
451e23598f2Sbriggs 		 * Get the source address aligned.
452e23598f2Sbriggs 		 */
453*d1579b2dSriastradh 		resid = count = uimin(pending_5380_count,
454229c36d3Sbriggs 				    4 - (((int) pending_5380_data) & 0x3));
455229c36d3Sbriggs 		if (count && (count < 4)) {
456e23598f2Sbriggs 			data = (u_int8_t *) pending_5380_data;
45761f16670Sjmc 			drq = (volatile u_int8_t *) ncr_5380_with_drq;
458e23598f2Sbriggs 			while (count) {
459e23598f2Sbriggs #define W1	*drq++ = *data++
460e23598f2Sbriggs 				W1; count--;
461e23598f2Sbriggs #undef W1
462e23598f2Sbriggs 			}
463e23598f2Sbriggs 			pending_5380_data += resid;
464e23598f2Sbriggs 			pending_5380_count -= resid;
465e23598f2Sbriggs 		}
466e23598f2Sbriggs 
467e23598f2Sbriggs 		/*
468e23598f2Sbriggs 		 * Get ready to start the transfer.
469e23598f2Sbriggs 		 */
47023216939Sbriggs 		while (pending_5380_count) {
47123216939Sbriggs 		int dcount;
47223216939Sbriggs 
473*d1579b2dSriastradh 		dcount = count = uimin(pending_5380_count, MIN_PHYS);
474e23598f2Sbriggs 		long_drq = (volatile u_int32_t *) ncr_5380_with_drq;
475c1138ce6Sbriggs 		long_data = (u_int32_t *) pending_5380_data;
476e23598f2Sbriggs 
477e23598f2Sbriggs #define W4	*long_drq++ = *long_data++
47816865d71Sbriggs 		while ( count > 64 ) {
479e23598f2Sbriggs 			W4; W4; W4; W4; W4; W4; W4; W4;
48023216939Sbriggs 			W4; W4; W4; W4; W4; W4; W4; W4; /*  64 */
481e23598f2Sbriggs 			count -= 64;
482e23598f2Sbriggs 		}
48316865d71Sbriggs 		while ( count > 8 ) {
48416865d71Sbriggs 			W4; W4;
48516865d71Sbriggs 			count -= 8;
486e23598f2Sbriggs 		}
487e23598f2Sbriggs #undef W4
488e23598f2Sbriggs 		data = (u_int8_t *) long_data;
48961f16670Sjmc 		drq = (volatile u_int8_t *) long_drq;
490e23598f2Sbriggs 		while (count) {
491e23598f2Sbriggs #define W1	*drq++ = *data++
492e23598f2Sbriggs 			W1; count--;
493e23598f2Sbriggs #undef W1
494e23598f2Sbriggs 		}
49523216939Sbriggs 		pending_5380_count -= dcount;
496c1138ce6Sbriggs 		pending_5380_data += dcount;
49723216939Sbriggs 		}
49816865d71Sbriggs 
499014b4041Sbriggs 		PID("write complete");
500014b4041Sbriggs 
501652316b5Smartin 		(void)*((volatile u_int8_t *) ncr_5380_with_drq);
50216865d71Sbriggs 
50316865d71Sbriggs 		PID("read a byte to force a phase change");
50416865d71Sbriggs 	}
50516865d71Sbriggs 
506d25f63a7Sbriggs 	/*
507d25f63a7Sbriggs 	 * OK.  No bus error occurred above.  Clear the nofault flag
508d25f63a7Sbriggs 	 * so we no longer short-circuit bus errors.
509d25f63a7Sbriggs 	 */
510d25f63a7Sbriggs 	nofault = (int *) 0;
511d25f63a7Sbriggs 
5122a878c9aSbriggs 	PID("end drq");
513014b4041Sbriggs 	return;
514014b4041Sbriggs #else
515014b4041Sbriggs 	return;
516a7783604Sbriggs #endif	/* if USE_PDMA */
517a7783604Sbriggs }
518a7783604Sbriggs 
519014b4041Sbriggs static void
ncr5380_drq_intr(void * p)5207acd68b1Schs ncr5380_drq_intr(void *p)
521014b4041Sbriggs {
522014b4041Sbriggs 	while (GET_5380_REG(NCR5380_DMSTAT) & SC_DMA_REQ) {
523014b4041Sbriggs 		do_ncr5380_drq_intr(p);
524014b4041Sbriggs 		scsi_clear_drq();
525014b4041Sbriggs 	}
526014b4041Sbriggs }
527014b4041Sbriggs 
528a7783604Sbriggs #if USE_PDMA
529e46e8e7aSbriggs 
530e46e8e7aSbriggs #define SCSI_TIMEOUT_VAL	10000000
531e46e8e7aSbriggs 
532e46e8e7aSbriggs static int
transfer_pdma(u_char * phasep,u_char * data,u_long * count)5337acd68b1Schs transfer_pdma(u_char *phasep, u_char *data, u_long *count)
534e46e8e7aSbriggs {
535e46e8e7aSbriggs 	SC_REQ *reqp = connected;
53637163421Sbriggs 	int len = *count, s, scsi_timeout = SCSI_TIMEOUT_VAL;
537e46e8e7aSbriggs 
538a7783604Sbriggs 	if (pdma_5380_dir) {
539e46e8e7aSbriggs 		panic("ncrscsi: transfer_pdma called when operation already "
5400f09ed48Sprovos 			"pending.");
541e46e8e7aSbriggs 	}
5422a878c9aSbriggs 	PID("transfer_pdma0")
543e46e8e7aSbriggs 
544bee422d9Sbriggs 	/*
545e23598f2Sbriggs  	 * Don't bother with PDMA if we can't sleep or for small transfers.
546bee422d9Sbriggs  	 */
547a106882aSbriggs 	if (reqp->dr_flag & DRIVER_NOINT) {
5482a878c9aSbriggs 		PID("pdma, falling back to transfer_pio.")
549d8dc3d91Sbriggs 		transfer_pio(phasep, data, count, 0);
550bee422d9Sbriggs 		return -1;
551e46e8e7aSbriggs 	}
552e46e8e7aSbriggs 
553e46e8e7aSbriggs 	/*
554bee422d9Sbriggs 	 * We are probably already at spl2(), so this is likely a no-op.
555bee422d9Sbriggs 	 * Paranoia.
556bee422d9Sbriggs 	 */
557bee422d9Sbriggs 	s = splbio();
558bee422d9Sbriggs 
559e23598f2Sbriggs 	scsi_idisable();
560e23598f2Sbriggs 
561e23598f2Sbriggs 	/*
562e23598f2Sbriggs 	 * Match phases with target.
563e23598f2Sbriggs 	 */
564e23598f2Sbriggs 	SET_5380_REG(NCR5380_TCOM, *phasep);
565e23598f2Sbriggs 
566bee422d9Sbriggs 	/*
567bee422d9Sbriggs 	 * Clear pending interrupts.
568bee422d9Sbriggs 	 */
569e46e8e7aSbriggs 	scsi_clr_ipend();
570e46e8e7aSbriggs 
571e46e8e7aSbriggs 	/*
572e46e8e7aSbriggs 	 * Wait until target asserts BSY.
573e46e8e7aSbriggs 	 */
574e23598f2Sbriggs 	while (    ((GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY) == 0)
575e23598f2Sbriggs 		&& (--scsi_timeout) );
576e46e8e7aSbriggs 	if (!scsi_timeout) {
577e46e8e7aSbriggs #if DIAGNOSTIC
57840ecbf8eSchristos 		printf("scsi timeout: waiting for BSY in %s.\n",
579e23598f2Sbriggs 			(*phasep == PH_DATAOUT) ? "pdma_out" : "pdma_in");
580e46e8e7aSbriggs #endif
581e46e8e7aSbriggs 		goto scsi_timeout_error;
582e46e8e7aSbriggs 	}
583e46e8e7aSbriggs 
584e46e8e7aSbriggs 	/*
585bee422d9Sbriggs 	 * Tell the driver that we're in DMA mode.
586bee422d9Sbriggs 	 */
587bee422d9Sbriggs 	reqp->dr_flag |= DRIVER_IN_DMA;
588bee422d9Sbriggs 
589bee422d9Sbriggs 	/*
590a7783604Sbriggs 	 * Load transfer values for DRQ interrupt handlers.
591e46e8e7aSbriggs 	 */
592a7783604Sbriggs 	pending_5380_data = data;
593e46e8e7aSbriggs 	pending_5380_count = len;
594e46e8e7aSbriggs 
595e46e8e7aSbriggs 	/*
596e46e8e7aSbriggs 	 * Set the transfer function to be called on DRQ interrupts.
597bee422d9Sbriggs 	 * And note that we're waiting.
598e46e8e7aSbriggs 	 */
599a7783604Sbriggs 	switch (*phasep) {
600a7783604Sbriggs 	default:
6010f09ed48Sprovos 		panic("Unexpected phase in transfer_pdma.");
602a7783604Sbriggs 	case PH_DATAOUT:
603a7783604Sbriggs 		pdma_5380_dir = 1;
604229c36d3Sbriggs 		SET_5380_REG(NCR5380_ICOM, GET_5380_REG(NCR5380_ICOM)|SC_ADTB);
605229c36d3Sbriggs 		SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE)|SC_M_DMA);
606e23598f2Sbriggs 		SET_5380_REG(NCR5380_DMSTAT, 0);
607a7783604Sbriggs 		break;
608a7783604Sbriggs 	case PH_DATAIN:
609a7783604Sbriggs 		pdma_5380_dir = 2;
610229c36d3Sbriggs 		SET_5380_REG(NCR5380_ICOM, 0);
611229c36d3Sbriggs 		SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE)|SC_M_DMA);
612e46e8e7aSbriggs 		SET_5380_REG(NCR5380_IRCV, 0);
613e23598f2Sbriggs 		break;
614e46e8e7aSbriggs 	}
615e46e8e7aSbriggs 
6162a878c9aSbriggs 	PID("waiting for interrupt.")
617229c36d3Sbriggs 
618e46e8e7aSbriggs 	/*
619e46e8e7aSbriggs 	 * Now that we're set up, enable interrupts and drop processor
620bee422d9Sbriggs 	 * priority back down.
621e46e8e7aSbriggs 	 */
622e46e8e7aSbriggs 	scsi_ienable();
623e46e8e7aSbriggs 	splx(s);
624bee422d9Sbriggs 	return 0;
625e46e8e7aSbriggs 
626e46e8e7aSbriggs scsi_timeout_error:
627e46e8e7aSbriggs 	/*
628e46e8e7aSbriggs 	 * Clear the DMA mode.
629e46e8e7aSbriggs 	 */
630e46e8e7aSbriggs 	SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE) & ~SC_M_DMA);
631e46e8e7aSbriggs 	return -1;
632e46e8e7aSbriggs }
633e46e8e7aSbriggs #endif /* if USE_PDMA */
634e46e8e7aSbriggs 
635e46e8e7aSbriggs /* Include general routines. */
636166af003Sbriggs #include <mac68k/dev/ncr5380.c>
637