xref: /netbsd-src/sys/arch/amiga/dev/flsc.c (revision d0fed6c87ddc40a8bffa6f99e7433ddfc864dd83)
1 /*	$NetBSD: flsc.c,v 1.14 1996/12/23 09:10:00 veego 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 *, struct cfdata *, 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, cfp, auxp)
101 	struct device	*pdp;
102 	struct cfdata	*cfp;
103 	void		*auxp;
104 {
105 	struct zbus_args *zap;
106 
107 	if (!is_a4000() && !is_a3000())
108 		return(0);
109 
110 	zap = auxp;
111 	if (zap->manid == 0x2140 && zap->prodid == 11
112 	    && iszthreepa(zap->pa))
113 		return(1);
114 
115 	return(0);
116 }
117 
118 void
119 flscattach(pdp, dp, auxp)
120 	struct device	*pdp;
121 	struct device	*dp;
122 	void		*auxp;
123 {
124 	struct flsc_softc *sc;
125 	struct zbus_args  *zap;
126 	flsc_regmap_p	   rp;
127 	vu_char		  *fas;
128 
129 	zap = auxp;
130 	fas = &((vu_char *)zap->va)[0x1000001];
131 
132 	sc = (struct flsc_softc *)dp;
133 	rp = &sc->sc_regmap;
134 
135 	rp->FAS216.sfas_tc_low	= &fas[0x00];
136 	rp->FAS216.sfas_tc_mid	= &fas[0x04];
137 	rp->FAS216.sfas_fifo	= &fas[0x08];
138 	rp->FAS216.sfas_command	= &fas[0x0C];
139 	rp->FAS216.sfas_dest_id	= &fas[0x10];
140 	rp->FAS216.sfas_timeout	= &fas[0x14];
141 	rp->FAS216.sfas_syncper	= &fas[0x18];
142 	rp->FAS216.sfas_syncoff	= &fas[0x1C];
143 	rp->FAS216.sfas_config1	= &fas[0x20];
144 	rp->FAS216.sfas_clkconv	= &fas[0x24];
145 	rp->FAS216.sfas_test	= &fas[0x28];
146 	rp->FAS216.sfas_config2	= &fas[0x2C];
147 	rp->FAS216.sfas_config3	= &fas[0x30];
148 	rp->FAS216.sfas_tc_high	= &fas[0x38];
149 	rp->FAS216.sfas_fifo_bot = &fas[0x3C];
150 	rp->hardbits		= &fas[0x40];
151 	rp->clear		= &fas[0x80];
152 	rp->dmabase		= zap->va;
153 
154 	sc->sc_softc.sc_fas	= (sfas_regmap_p)rp;
155 	sc->sc_softc.sc_spec	= &sc->sc_specific;
156 
157 	sc->sc_softc.sc_led	= flsc_led;
158 
159 	sc->sc_softc.sc_setup_dma	= flsc_setup_dma;
160 	sc->sc_softc.sc_build_dma_chain = flsc_build_dma_chain;
161 	sc->sc_softc.sc_need_bump	= flsc_need_bump;
162 
163 	sc->sc_softc.sc_clock_freq   = 40;   /* FastlaneZ3 runs at 40MHz */
164 	sc->sc_softc.sc_timeout      = 250;  /* Set default timeout to 250ms */
165 	sc->sc_softc.sc_config_flags = 0;    /* No config flags yet */
166 	sc->sc_softc.sc_host_id      = 7;    /* Should check the jumpers */
167 
168 	sc->sc_specific.portbits = 0xA0 | FLSC_PB_EDI | FLSC_PB_ESI;
169 	sc->sc_specific.hardbits = *rp->hardbits;
170 
171 	sc->sc_softc.sc_bump_sz = NBPG;
172 	sc->sc_softc.sc_bump_pa = 0x0;
173 
174 	sfasinitialize((struct sfas_softc *)sc);
175 
176 	sc->sc_softc.sc_link.channel	    = SCSI_CHANNEL_ONLY_ONE;
177 	sc->sc_softc.sc_link.adapter_softc  = sc;
178 	sc->sc_softc.sc_link.adapter_target = sc->sc_softc.sc_host_id;
179 	sc->sc_softc.sc_link.adapter	    = &flsc_scsiswitch;
180 	sc->sc_softc.sc_link.device	    = &flsc_scsidev;
181 	sc->sc_softc.sc_link.openings	    = 1;
182 	sc->sc_softc.sc_link.max_target     = 7;
183 
184 	sc->sc_softc.sc_isr.isr_intr = flsc_intr;
185 	sc->sc_softc.sc_isr.isr_arg  = &sc->sc_softc;
186 	sc->sc_softc.sc_isr.isr_ipl  = 2;
187 	add_isr(&sc->sc_softc.sc_isr);
188 
189 /* We don't want interrupt until we're initialized! */
190 	*rp->hardbits = sc->sc_specific.portbits;
191 
192 	printf("\n");
193 
194 /* attach all scsi units on us */
195 	config_found(dp, &sc->sc_softc.sc_link, scsiprint);
196 }
197 
198 int
199 flsc_intr(arg)
200 	void *arg;
201 {
202 	struct sfas_softc *dev = arg;
203 	flsc_regmap_p	      rp;
204 	struct flsc_specific *flspec;
205 	int		      quickints;
206 	u_char		      hb;
207 
208 	flspec = dev->sc_spec;
209 	rp = (flsc_regmap_p)dev->sc_fas;
210 	hb = *rp->hardbits;
211 
212 	if (hb & FLSC_HB_IACT)
213 		return(0);
214 
215 	flspec->hardbits = hb;
216 	if ((hb & FLSC_HB_CREQ) &&
217 	    !(hb & FLSC_HB_MINT) &&
218 	    (*rp->FAS216.sfas_status & SFAS_STAT_INTERRUPT_PENDING)) {
219 		quickints = 16;
220 		do {
221 			dev->sc_status = *rp->FAS216.sfas_status;
222 			dev->sc_interrupt = *rp->FAS216.sfas_interrupt;
223 
224 			if (dev->sc_interrupt & SFAS_INT_RESELECTED) {
225 				dev->sc_resel[0] = *rp->FAS216.sfas_fifo;
226 				dev->sc_resel[1] = *rp->FAS216.sfas_fifo;
227 			}
228 			sfasintr(dev);
229 
230 		} while((*rp->FAS216.sfas_status & SFAS_STAT_INTERRUPT_PENDING)
231 			&& --quickints);
232 	}
233 
234 	/* Reset fastlane interrupt bits */
235 	*rp->hardbits = flspec->portbits & ~FLSC_PB_INT_BITS;
236 	*rp->hardbits = flspec->portbits;
237 
238 	return(1);
239 }
240 
241 /* Load transfer adress into dma register */
242 void
243 flsc_set_dma_adr(sc, ptr)
244 	struct sfas_softc *sc;
245 	vm_offset_t	  ptr;
246 {
247 	flsc_regmap_p	rp;
248 	unsigned int   *p;
249 	unsigned int	d;
250 
251 	rp = (flsc_regmap_p)sc->sc_fas;
252 
253 	d = (unsigned int)ptr;
254 	p = (unsigned int *)((d & 0xFFFFFF) + (int)rp->dmabase);
255 
256 	*rp->clear=0;
257 	*p = d;
258 }
259 
260 /* Set DMA transfer counter */
261 void
262 flsc_set_dma_tc(sc, len)
263 	struct sfas_softc *sc;
264 	unsigned int	  len;
265 {
266 	*sc->sc_fas->sfas_tc_low  = len; len >>= 8;
267 	*sc->sc_fas->sfas_tc_mid  = len; len >>= 8;
268 	*sc->sc_fas->sfas_tc_high = len;
269 }
270 
271 /* Set DMA mode */
272 void
273 flsc_set_dma_mode(sc, mode)
274 	struct sfas_softc *sc;
275 	int		  mode;
276 {
277 	struct flsc_specific *spec;
278 
279 	spec = sc->sc_spec;
280 
281 	spec->portbits = (spec->portbits & ~FLSC_PB_DMA_BITS) | mode;
282 	*((flsc_regmap_p)sc->sc_fas)->hardbits = spec->portbits;
283 }
284 
285 /* Initialize DMA for transfer */
286 int
287 flsc_setup_dma(sc, ptr, len, mode)
288 	struct sfas_softc *sc;
289 	vm_offset_t	  ptr;
290 	int		  len;
291 	int		  mode;
292 {
293 	int	retval;
294 
295 	retval = 0;
296 
297 	switch(mode) {
298 	case SFAS_DMA_READ:
299 	case SFAS_DMA_WRITE:
300 		flsc_set_dma_adr(sc, ptr);
301 		if (mode == SFAS_DMA_READ)
302 		  flsc_set_dma_mode(sc,FLSC_PB_ENABLE_DMA | FLSC_PB_DMA_READ);
303 		else
304 		  flsc_set_dma_mode(sc,FLSC_PB_ENABLE_DMA | FLSC_PB_DMA_WRITE);
305 
306 		flsc_set_dma_tc(sc, len);
307 		break;
308 
309 	case SFAS_DMA_CLEAR:
310 	default:
311 		flsc_set_dma_mode(sc, FLSC_PB_DISABLE_DMA);
312 		flsc_set_dma_adr(sc, 0);
313 
314 		retval = (*sc->sc_fas->sfas_tc_high << 16) |
315 			 (*sc->sc_fas->sfas_tc_mid  <<  8) |
316 			  *sc->sc_fas->sfas_tc_low;
317 
318 		flsc_set_dma_tc(sc, 0);
319 		break;
320 	}
321 
322 	return(retval);
323 }
324 
325 /* Check if address and len is ok for DMA transfer */
326 int
327 flsc_need_bump(sc, ptr, len)
328 	struct sfas_softc *sc;
329 	vm_offset_t	  ptr;
330 	int		  len;
331 {
332 	int	p;
333 
334 	if (((int)ptr & 0x03) || (len & 0x03)) {
335 		if (len < 256)
336 			p = len;
337 		else
338 			p = 256;
339 	} else
340 		p = 0;
341 
342 	return(p);
343 }
344 
345 /* Interrupt driven routines */
346 int
347 flsc_build_dma_chain(sc, chain, p, l)
348 	struct sfas_softc	*sc;
349 	struct sfas_dma_chain	*chain;
350 	void			*p;
351 	int			 l;
352 {
353 	vm_offset_t  pa, lastpa;
354 	char	    *ptr;
355 	int	     len, prelen, max_t, n;
356 
357 	if (l == 0)
358 		return(0);
359 
360 #define set_link(n, p, l, f)\
361 do { chain[n].ptr = (p); chain[n].len = (l); chain[n++].flg = (f); } while(0)
362 
363 	n = 0;
364 
365 	if (l < 512)
366 		set_link(n, (vm_offset_t)p, l, SFAS_CHAIN_BUMP);
367 	else if ((p >= (void *)0xFF000000)
368 #if defined(M68040) || defined(M68060)
369 		 && ((mmutype == MMU_68040) && (p >= (void *)0xFFFC0000))
370 #endif
371 		 ) {
372 		while(l != 0) {
373 			len = ((l > sc->sc_bump_sz) ? sc->sc_bump_sz : l);
374 
375 			set_link(n, (vm_offset_t)p, len, SFAS_CHAIN_BUMP);
376 
377 			p += len;
378 			l -= len;
379 		}
380 	} else {
381 		ptr = p;
382 		len = l;
383 
384 		pa = kvtop(ptr);
385 		prelen = ((int)ptr & 0x03);
386 
387 		if (prelen) {
388 			prelen = 4-prelen;
389 			set_link(n, (vm_offset_t)ptr, prelen, SFAS_CHAIN_BUMP);
390 			ptr += prelen;
391 			len -= prelen;
392 		}
393 
394 		lastpa = 0;
395 		while(len > 3) {
396 			pa = kvtop(ptr);
397 			max_t = NBPG - (pa & PGOFSET);
398 			if (max_t > len)
399 			  max_t = len;
400 
401 			max_t &= ~3;
402 
403 			if (lastpa == pa)
404 				sc->sc_chain[n-1].len += max_t;
405 			else
406 				set_link(n, pa, max_t, SFAS_CHAIN_DMA);
407 
408 			lastpa = pa+max_t;
409 
410 			ptr += max_t;
411 			len -= max_t;
412 		}
413 
414 		if (len)
415 			set_link(n, (vm_offset_t)ptr, len, SFAS_CHAIN_BUMP);
416 	}
417 
418 	return(n);
419 }
420 
421 /* Turn on/off led */
422 void
423 flsc_led(sc, mode)
424 	struct sfas_softc *sc;
425 	int		  mode;
426 {
427 	struct flsc_specific   *spec;
428 	flsc_regmap_p		rp;
429 
430 	spec = sc->sc_spec;
431 	rp = (flsc_regmap_p)sc->sc_fas;
432 
433 	if (mode) {
434 		sc->sc_led_status++;
435 
436 		spec->portbits |= FLSC_PB_LED;
437 		*rp->hardbits = spec->portbits;
438 	} else {
439 		if (sc->sc_led_status)
440 			sc->sc_led_status--;
441 
442 		if (!sc->sc_led_status) {
443 			spec->portbits &= ~FLSC_PB_LED;
444 			*rp->hardbits = spec->portbits;
445 		}
446 	}
447 }
448