xref: /netbsd-src/sys/arch/amiga/dev/flsc.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: flsc.c,v 1.12 1996/10/13 03:06:57 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Daniel Widenfalk
5  * Copyright (c) 1994 Christian E. Hopps
6  * Copyright (c) 1982, 1990 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)dma.c
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/device.h>
44 #include <scsi/scsi_all.h>
45 #include <scsi/scsiconf.h>
46 #include <vm/vm.h>
47 #include <vm/vm_kern.h>
48 #include <vm/vm_page.h>
49 #include <machine/pmap.h>
50 #include <amiga/amiga/custom.h>
51 #include <amiga/amiga/cc.h>
52 #include <amiga/amiga/device.h>
53 #include <amiga/amiga/isr.h>
54 #include <amiga/dev/sfasreg.h>
55 #include <amiga/dev/sfasvar.h>
56 #include <amiga/dev/zbusvar.h>
57 #include <amiga/dev/flscreg.h>
58 #include <amiga/dev/flscvar.h>
59 
60 void flscattach __P((struct device *, struct device *, void *));
61 int  flscmatch  __P((struct device *, void *, void *));
62 
63 struct scsi_adapter flsc_scsiswitch = {
64 	sfas_scsicmd,
65 	sfas_minphys,
66 	0,			/* no lun support */
67 	0,			/* no lun support */
68 };
69 
70 struct scsi_device flsc_scsidev = {
71 	NULL,		/* use default error handler */
72 	NULL,		/* do not have a start functio */
73 	NULL,		/* have no async handler */
74 	NULL,		/* Use default done routine */
75 };
76 
77 struct cfattach flsc_ca = {
78 	sizeof(struct flsc_softc), flscmatch, flscattach
79 };
80 
81 struct cfdriver flsc_cd = {
82 	NULL, "flsc", DV_DULL, NULL, 0
83 };
84 
85 int flsc_intr		 __P((void *));
86 void flsc_set_dma_adr	 __P((struct sfas_softc *sc, vm_offset_t ptr));
87 void flsc_set_dma_tc	 __P((struct sfas_softc *sc, unsigned int len));
88 void flsc_set_dma_mode	 __P((struct sfas_softc *sc, int mode));
89 int flsc_setup_dma	 __P((struct sfas_softc *sc, vm_offset_t ptr, int len,
90 			      int mode));
91 int flsc_build_dma_chain __P((struct sfas_softc *sc,
92 			      struct sfas_dma_chain *chain, void *p, int l));
93 int flsc_need_bump	 __P((struct sfas_softc *sc, vm_offset_t ptr, int len));
94 void flsc_led		 __P((struct sfas_softc *sc, int mode));
95 
96 /*
97  * if we are an Advanced Systems & Software FastlaneZ3
98  */
99 int
100 flscmatch(pdp, match, auxp)
101 	struct device	*pdp;
102 	void		*match, *auxp;
103 {
104 	struct zbus_args *zap;
105 
106 	if (!is_a4000() && !is_a3000())
107 		return(0);
108 
109 	zap = auxp;
110 	if (zap->manid == 0x2140 && zap->prodid == 11
111 	    && iszthreepa(zap->pa))
112 		return(1);
113 
114 	return(0);
115 }
116 
117 void
118 flscattach(pdp, dp, auxp)
119 	struct device	*pdp;
120 	struct device	*dp;
121 	void		*auxp;
122 {
123 	struct flsc_softc *sc;
124 	struct zbus_args  *zap;
125 	flsc_regmap_p	   rp;
126 	vu_char		  *fas;
127 
128 	zap = auxp;
129 	fas = &((vu_char *)zap->va)[0x1000001];
130 
131 	sc = (struct flsc_softc *)dp;
132 	rp = &sc->sc_regmap;
133 
134 	rp->FAS216.sfas_tc_low	= &fas[0x00];
135 	rp->FAS216.sfas_tc_mid	= &fas[0x04];
136 	rp->FAS216.sfas_fifo	= &fas[0x08];
137 	rp->FAS216.sfas_command	= &fas[0x0C];
138 	rp->FAS216.sfas_dest_id	= &fas[0x10];
139 	rp->FAS216.sfas_timeout	= &fas[0x14];
140 	rp->FAS216.sfas_syncper	= &fas[0x18];
141 	rp->FAS216.sfas_syncoff	= &fas[0x1C];
142 	rp->FAS216.sfas_config1	= &fas[0x20];
143 	rp->FAS216.sfas_clkconv	= &fas[0x24];
144 	rp->FAS216.sfas_test	= &fas[0x28];
145 	rp->FAS216.sfas_config2	= &fas[0x2C];
146 	rp->FAS216.sfas_config3	= &fas[0x30];
147 	rp->FAS216.sfas_tc_high	= &fas[0x38];
148 	rp->FAS216.sfas_fifo_bot = &fas[0x3C];
149 	rp->hardbits		= &fas[0x40];
150 	rp->clear		= &fas[0x80];
151 	rp->dmabase		= zap->va;
152 
153 	sc->sc_softc.sc_fas	= (sfas_regmap_p)rp;
154 	sc->sc_softc.sc_spec	= &sc->sc_specific;
155 
156 	sc->sc_softc.sc_led	= flsc_led;
157 
158 	sc->sc_softc.sc_setup_dma	= flsc_setup_dma;
159 	sc->sc_softc.sc_build_dma_chain = flsc_build_dma_chain;
160 	sc->sc_softc.sc_need_bump	= flsc_need_bump;
161 
162 	sc->sc_softc.sc_clock_freq   = 40;   /* FastlaneZ3 runs at 40MHz */
163 	sc->sc_softc.sc_timeout      = 250;  /* Set default timeout to 250ms */
164 	sc->sc_softc.sc_config_flags = 0;    /* No config flags yet */
165 	sc->sc_softc.sc_host_id      = 7;    /* Should check the jumpers */
166 
167 	sc->sc_specific.portbits = 0xA0 | FLSC_PB_EDI | FLSC_PB_ESI;
168 	sc->sc_specific.hardbits = *rp->hardbits;
169 
170 	sc->sc_softc.sc_bump_sz = NBPG;
171 	sc->sc_softc.sc_bump_pa = 0x0;
172 
173 	sfasinitialize((struct sfas_softc *)sc);
174 
175 	sc->sc_softc.sc_link.channel	    = SCSI_CHANNEL_ONLY_ONE;
176 	sc->sc_softc.sc_link.adapter_softc  = sc;
177 	sc->sc_softc.sc_link.adapter_target = sc->sc_softc.sc_host_id;
178 	sc->sc_softc.sc_link.adapter	    = &flsc_scsiswitch;
179 	sc->sc_softc.sc_link.device	    = &flsc_scsidev;
180 	sc->sc_softc.sc_link.openings	    = 1;
181 
182 	sc->sc_softc.sc_isr.isr_intr = flsc_intr;
183 	sc->sc_softc.sc_isr.isr_arg  = &sc->sc_softc;
184 	sc->sc_softc.sc_isr.isr_ipl  = 2;
185 	add_isr(&sc->sc_softc.sc_isr);
186 
187 /* We don't want interrupt until we're initialized! */
188 	*rp->hardbits = sc->sc_specific.portbits;
189 
190 	printf("\n");
191 
192 /* attach all scsi units on us */
193 	config_found(dp, &sc->sc_softc.sc_link, scsiprint);
194 }
195 
196 int
197 flsc_intr(arg)
198 	void *arg;
199 {
200 	struct sfas_softc *dev = arg;
201 	flsc_regmap_p	      rp;
202 	struct flsc_specific *flspec;
203 	int		      quickints;
204 	u_char		      hb;
205 
206 	flspec = dev->sc_spec;
207 	rp = (flsc_regmap_p)dev->sc_fas;
208 	hb = *rp->hardbits;
209 
210 	if (hb & FLSC_HB_IACT)
211 		return(0);
212 
213 	flspec->hardbits = hb;
214 	if ((hb & FLSC_HB_CREQ) &&
215 	    !(hb & FLSC_HB_MINT) &&
216 	    (*rp->FAS216.sfas_status & SFAS_STAT_INTERRUPT_PENDING)) {
217 		quickints = 16;
218 		do {
219 			dev->sc_status = *rp->FAS216.sfas_status;
220 			dev->sc_interrupt = *rp->FAS216.sfas_interrupt;
221 
222 			if (dev->sc_interrupt & SFAS_INT_RESELECTED) {
223 				dev->sc_resel[0] = *rp->FAS216.sfas_fifo;
224 				dev->sc_resel[1] = *rp->FAS216.sfas_fifo;
225 			}
226 			sfasintr(dev);
227 
228 		} while((*rp->FAS216.sfas_status & SFAS_STAT_INTERRUPT_PENDING)
229 			&& --quickints);
230 	}
231 
232 	/* Reset fastlane interrupt bits */
233 	*rp->hardbits = flspec->portbits & ~FLSC_PB_INT_BITS;
234 	*rp->hardbits = flspec->portbits;
235 
236 	return(1);
237 }
238 
239 /* Load transfer adress into dma register */
240 void
241 flsc_set_dma_adr(sc, ptr)
242 	struct sfas_softc *sc;
243 	vm_offset_t	  ptr;
244 {
245 	flsc_regmap_p	rp;
246 	unsigned int   *p;
247 	unsigned int	d;
248 
249 	rp = (flsc_regmap_p)sc->sc_fas;
250 
251 	d = (unsigned int)ptr;
252 	p = (unsigned int *)((d & 0xFFFFFF) + (int)rp->dmabase);
253 
254 	*rp->clear=0;
255 	*p = d;
256 }
257 
258 /* Set DMA transfer counter */
259 void
260 flsc_set_dma_tc(sc, len)
261 	struct sfas_softc *sc;
262 	unsigned int	  len;
263 {
264 	*sc->sc_fas->sfas_tc_low  = len; len >>= 8;
265 	*sc->sc_fas->sfas_tc_mid  = len; len >>= 8;
266 	*sc->sc_fas->sfas_tc_high = len;
267 }
268 
269 /* Set DMA mode */
270 void
271 flsc_set_dma_mode(sc, mode)
272 	struct sfas_softc *sc;
273 	int		  mode;
274 {
275 	struct flsc_specific *spec;
276 
277 	spec = sc->sc_spec;
278 
279 	spec->portbits = (spec->portbits & ~FLSC_PB_DMA_BITS) | mode;
280 	*((flsc_regmap_p)sc->sc_fas)->hardbits = spec->portbits;
281 }
282 
283 /* Initialize DMA for transfer */
284 int
285 flsc_setup_dma(sc, ptr, len, mode)
286 	struct sfas_softc *sc;
287 	vm_offset_t	  ptr;
288 	int		  len;
289 	int		  mode;
290 {
291 	int	retval;
292 
293 	retval = 0;
294 
295 	switch(mode) {
296 	case SFAS_DMA_READ:
297 	case SFAS_DMA_WRITE:
298 		flsc_set_dma_adr(sc, ptr);
299 		if (mode == SFAS_DMA_READ)
300 		  flsc_set_dma_mode(sc,FLSC_PB_ENABLE_DMA | FLSC_PB_DMA_READ);
301 		else
302 		  flsc_set_dma_mode(sc,FLSC_PB_ENABLE_DMA | FLSC_PB_DMA_WRITE);
303 
304 		flsc_set_dma_tc(sc, len);
305 		break;
306 
307 	case SFAS_DMA_CLEAR:
308 	default:
309 		flsc_set_dma_mode(sc, FLSC_PB_DISABLE_DMA);
310 		flsc_set_dma_adr(sc, 0);
311 
312 		retval = (*sc->sc_fas->sfas_tc_high << 16) |
313 			 (*sc->sc_fas->sfas_tc_mid  <<  8) |
314 			  *sc->sc_fas->sfas_tc_low;
315 
316 		flsc_set_dma_tc(sc, 0);
317 		break;
318 	}
319 
320 	return(retval);
321 }
322 
323 /* Check if address and len is ok for DMA transfer */
324 int
325 flsc_need_bump(sc, ptr, len)
326 	struct sfas_softc *sc;
327 	vm_offset_t	  ptr;
328 	int		  len;
329 {
330 	int	p;
331 
332 	if (((int)ptr & 0x03) || (len & 0x03)) {
333 		if (len < 256)
334 			p = len;
335 		else
336 			p = 256;
337 	} else
338 		p = 0;
339 
340 	return(p);
341 }
342 
343 /* Interrupt driven routines */
344 int
345 flsc_build_dma_chain(sc, chain, p, l)
346 	struct sfas_softc	*sc;
347 	struct sfas_dma_chain	*chain;
348 	void			*p;
349 	int			 l;
350 {
351 	vm_offset_t  pa, lastpa;
352 	char	    *ptr;
353 	int	     len, prelen, max_t, n;
354 
355 	if (l == 0)
356 		return(0);
357 
358 #define set_link(n, p, l, f)\
359 do { chain[n].ptr = (p); chain[n].len = (l); chain[n++].flg = (f); } while(0)
360 
361 	n = 0;
362 
363 	if (l < 512)
364 		set_link(n, (vm_offset_t)p, l, SFAS_CHAIN_BUMP);
365 	else if ((p >= (void *)0xFF000000)
366 #if defined(M68040) || defined(M68060)
367 		 && ((mmutype == MMU_68040) && (p >= (void *)0xFFFC0000))
368 #endif
369 		 ) {
370 		while(l != 0) {
371 			len = ((l > sc->sc_bump_sz) ? sc->sc_bump_sz : l);
372 
373 			set_link(n, (vm_offset_t)p, len, SFAS_CHAIN_BUMP);
374 
375 			p += len;
376 			l -= len;
377 		}
378 	} else {
379 		ptr = p;
380 		len = l;
381 
382 		pa = kvtop(ptr);
383 		prelen = ((int)ptr & 0x03);
384 
385 		if (prelen) {
386 			prelen = 4-prelen;
387 			set_link(n, (vm_offset_t)ptr, prelen, SFAS_CHAIN_BUMP);
388 			ptr += prelen;
389 			len -= prelen;
390 		}
391 
392 		lastpa = 0;
393 		while(len > 3) {
394 			pa = kvtop(ptr);
395 			max_t = NBPG - (pa & PGOFSET);
396 			if (max_t > len)
397 			  max_t = len;
398 
399 			max_t &= ~3;
400 
401 			if (lastpa == pa)
402 				sc->sc_chain[n-1].len += max_t;
403 			else
404 				set_link(n, pa, max_t, SFAS_CHAIN_DMA);
405 
406 			lastpa = pa+max_t;
407 
408 			ptr += max_t;
409 			len -= max_t;
410 		}
411 
412 		if (len)
413 			set_link(n, (vm_offset_t)ptr, len, SFAS_CHAIN_BUMP);
414 	}
415 
416 	return(n);
417 }
418 
419 /* Turn on/off led */
420 void
421 flsc_led(sc, mode)
422 	struct sfas_softc *sc;
423 	int		  mode;
424 {
425 	struct flsc_specific   *spec;
426 	flsc_regmap_p		rp;
427 
428 	spec = sc->sc_spec;
429 	rp = (flsc_regmap_p)sc->sc_fas;
430 
431 	if (mode) {
432 		sc->sc_led_status++;
433 
434 		spec->portbits |= FLSC_PB_LED;
435 		*rp->hardbits = spec->portbits;
436 	} else {
437 		if (sc->sc_led_status)
438 			sc->sc_led_status--;
439 
440 		if (!sc->sc_led_status) {
441 			spec->portbits &= ~FLSC_PB_LED;
442 			*rp->hardbits = spec->portbits;
443 		}
444 	}
445 }
446