xref: /original-bsd/sys/i386/isa/fd.c (revision 3705696b)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Don Ahn.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)fd.c	8.1 (Berkeley) 06/11/93
11  */
12 
13 #include "fd.h"
14 #if NFD > 0
15 /*
16  * This driver assumed that NFD == 2. Now it works for NFD == 1 or NFD == 2
17  * It will probably not work for NFD > 2.
18  */
19 #include <sys/param.h>
20 #include <sys/dkbad.h>
21 #include <sys/systm.h>
22 #include <sys/conf.h>
23 #include <sys/file.h>
24 #include <sys/ioctl.h>
25 #include <sys/buf.h>
26 #include <sys/uio.h>
27 
28 #include <i386/isa/isa_device.h>
29 #include <i386/isa/fdreg.h>
30 #include <i386/isa/icu.h>
31 
32 #define	FDUNIT(s)	((s)&1)
33 #define	FDTYPE(s)	(((s)>>1)&7)
34 #define FDMOTOR(u) 	(fd_unit[(u)].motor ? (1 << (4 + (u))) : 0)
35 
36 #define b_cylin b_resid
37 #define b_step b_resid
38 #define FDBLK 512
39 #define NUMTYPES 4
40 
41 struct fd_type {
42 	int	sectrac;		/* sectors per track         */
43 	int	secsize;		/* size code for sectors     */
44 	int	datalen;		/* data len when secsize = 0 */
45 	int	gap;			/* gap len between sectors   */
46 	int	tracks;			/* total num of tracks       */
47 	int	size;			/* size of disk in sectors   */
48 	int	steptrac;		/* steps per cylinder        */
49 	int	trans;			/* transfer speed code       */
50 };
51 
52 struct fd_type fd_types[NUMTYPES] = {
53  	{ 18,2,0xFF,0x1B,80,2880,1,0 },	/* 1.44 meg HD 3.5in floppy    */
54 	{ 15,2,0xFF,0x1B,80,2400,1,0 },	/* 1.2 meg HD floppy           */
55 	{ 9,2,0xFF,0x23,40,720,2,1 },	/* 360k floppy in 1.2meg drive */
56 	{ 9,2,0xFF,0x2A,40,720,1,1 },	/* 360k floppy in DD drive     */
57 };
58 
59 struct fd_u {
60 	int type;		/* Drive type (HD, DD     */
61 	int active;		/* Drive activity boolean */
62 	int motor;		/* Motor on flag          */
63 	struct buf head;	/* Head of buf chain      */
64 	struct buf rhead;	/* Raw head of buf chain  */
65 	int reset;
66 } fd_unit[NFD];
67 
68 
69 extern int hz;
70 
71 /* state needed for current transfer */
72 static fdc;	/* floppy disk controller io base register */
73 int	fd_dmachan = 2;
74 static int fd_skip;
75 static int fd_state;
76 static int fd_retry;
77 static int fd_drive;
78 static int fd_track = -1;
79 static int fd_status[7];
80 
81 /*
82 	make sure bounce buffer for DMA is aligned since the DMA chip
83 	doesn't roll over properly over a 64k boundary
84 */
85 extern struct buf *dma_bounce[];
86 
87 /****************************************************************************/
88 /*                      autoconfiguration stuff                             */
89 /****************************************************************************/
90 int fdprobe(), fdattach(), fd_turnoff();
91 
92 struct	isa_driver fddriver = {
93 	fdprobe, fdattach, "fd",
94 };
95 
96 fdprobe(dev)
97 struct isa_device *dev;
98 {
99 	return 1;
100 }
101 
102 fdattach(dev)
103 struct isa_device *dev;
104 {	int	s;
105 
106 	fdc = dev->id_iobase;
107 	/* Set transfer to 500kbps */
108 	outb(fdc+fdctl,0);
109 	fd_turnoff(0);
110 }
111 
112 int
113 Fdsize(dev)
114 dev_t	dev;
115 {
116 	return(2400);
117 }
118 
119 /****************************************************************************/
120 /*                               fdstrategy                                 */
121 /****************************************************************************/
122 Fdstrategy(bp)
123 	register struct buf *bp;	/* IO operation to perform */
124 {
125 	register struct buf *dp;
126 	long nblocks,blknum;
127  	int	unit, type, s;
128 
129  	unit = FDUNIT(minor(bp->b_dev));
130  	type = FDTYPE(minor(bp->b_dev));
131 
132 #ifdef FDTEST
133 printf("fdstrat%d, blk = %d, bcount = %d, addr = %x|",
134 	unit, bp->b_blkno, bp->b_bcount,bp->b_un.b_addr);
135 #endif
136 	if ((unit >= NFD) || (bp->b_blkno < 0)) {
137 		printf("fdstrat: unit = %d, blkno = %d, bcount = %d\n",
138 			unit, bp->b_blkno, bp->b_bcount);
139 		pg("fd:error in fdstrategy");
140 		bp->b_error = EINVAL;
141 		bp->b_flags |= B_ERROR;
142 		goto bad;
143 	}
144 	/*
145 	 * Set up block calculations.
146 	 */
147 	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
148  	nblocks = fd_types[type].size;
149 	if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
150 		if (blknum == nblocks) {
151 			bp->b_resid = bp->b_bcount;
152 		} else {
153 			bp->b_error = ENOSPC;
154 			bp->b_flags |= B_ERROR;
155 		}
156 #ifdef FDTEST
157 printf("fdstrat%d, too big\n");
158 #endif
159 		goto bad;
160 	}
161  	bp->b_cylin = blknum / (fd_types[type].sectrac * 2);
162 	dp = &fd_unit[unit].head;
163 	dp->b_step = (fd_types[fd_unit[unit].type].steptrac);
164 	s = splbio();
165 	disksort(dp, bp);
166 	if ((fd_unit[0].head.b_active == 0)
167 #if NFD > 1
168 	    && (fd_unit[1].head.b_active == 0)
169 #endif
170 	    ) {
171 #ifdef FDDEBUG
172 printf("T|");
173 #endif
174 		dp->b_active = 1;
175 		fd_drive = unit;
176 		fd_track = -1;  /* force seek on first xfer */
177 		untimeout(fd_turnoff,unit);
178 		fdstart(unit);		/* start drive if idle */
179 	}
180 	splx(s);
181 	return;
182 
183 bad:
184 	biodone(bp);
185 }
186 
187 /****************************************************************************/
188 /*                            motor control stuff                           */
189 /****************************************************************************/
190 set_motor(unit,reset)
191 int unit,reset;
192 {
193 	outb(fdc+fdout,unit | (reset ? 0 : 0xC)  | FDMOTOR(0)
194 #if NFD > 1
195 	     | FDMOTOR(1)
196 #endif
197 	     );
198 }
199 
200 fd_turnoff(unit)
201 int unit;
202 {
203 	fd_unit[unit].motor = 0;
204 	if (unit) set_motor(0,0);
205 	else set_motor(1,0);
206 }
207 
208 fd_turnon(unit)
209 int unit;
210 {
211 	fd_unit[unit].motor = 1;
212 	set_motor(unit,0);
213 }
214 
215 /****************************************************************************/
216 /*                             fdc in/out                                   */
217 /****************************************************************************/
218 int
219 in_fdc()
220 {
221 	int i;
222 	while ((i = inb(fdc+fdsts) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM))
223 		if (i == NE7_RQM) return -1;
224 	return inb(fdc+fddata);
225 }
226 
227 dump_stat()
228 {
229 	int i;
230 	for(i=0;i<7;i++) {
231 		fd_status[i] = in_fdc();
232 		if (fd_status[i] < 0) break;
233 	}
234 printf("FD bad status :%lx %lx %lx %lx %lx %lx %lx\n",
235 	fd_status[0], fd_status[1], fd_status[2], fd_status[3],
236 	fd_status[4], fd_status[5], fd_status[6] );
237 }
238 
239 out_fdc(x)
240 int x;
241 {
242 	int r,errcnt;
243 	static int maxcnt = 0;
244 	errcnt = 0;
245 	do {
246 		r = (inb(fdc+fdsts) & (NE7_DIO|NE7_RQM));
247 		if (r== NE7_RQM) break;
248 		if (r==(NE7_DIO|NE7_RQM)) {
249 			dump_stat(); /* error: direction. eat up output */
250 #ifdef FDOTHER
251 printf("%lx\n",x);
252 #endif
253 		}
254 		/* printf("Error r = %d:",r); */
255 		errcnt++;
256 	} while (1);
257 	if (errcnt > maxcnt) {
258 		maxcnt = errcnt;
259 #ifdef FDOTHER
260 printf("New MAX = %d\n",maxcnt);
261 #endif
262 	}
263 	outb(fdc+fddata,x);
264 }
265 
266 /* see if fdc responding */
267 int
268 check_fdc()
269 {
270 	int i;
271 	for(i=0;i<100;i++) {
272 		if (inb(fdc+fdsts)& NE7_RQM) return 0;
273 	}
274 	return 1;
275 }
276 
277 /****************************************************************************/
278 /*                           fdopen/fdclose                                 */
279 /****************************************************************************/
280 Fdopen(dev, flags)
281 	dev_t	dev;
282 	int	flags;
283 {
284  	int unit = FDUNIT(minor(dev));
285  	int type = FDTYPE(minor(dev));
286 	int s;
287 
288 	printf("fdopen %x %d %d\n", minor(dev), unit, type);
289 	/* check bounds */
290 	if (unit >= NFD) return(ENXIO);
291 	if (type >= NUMTYPES) return(ENXIO);
292 /*
293 	if (check_fdc()) return(EBUSY);
294 */
295 
296 	/* Set proper disk type, only allow one type */
297 	return 0;
298 }
299 
300 Fdclose(dev, flags)
301 	dev_t dev;
302 {
303 	return (0);
304 }
305 
306 /****************************************************************************/
307 /*                            fdread/fdwrite                                */
308 /****************************************************************************/
309 /*
310  * Routines to do raw IO for a unit.
311  */
312 Fdread(dev, uio)			/* character read routine */
313 dev_t dev;
314 struct uio *uio;
315 {
316  	int unit = FDUNIT(minor(dev)) ;
317 	if (unit >= NFD) return(ENXIO);
318 	return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_READ,minphys,uio));
319 }
320 
321 Fdwrite(dev, uio)			/* character write routine */
322 dev_t dev;
323 struct uio *uio;
324 {
325  	int unit = FDUNIT(minor(dev)) ;
326 	if (unit >= NFD) return(ENXIO);
327 	return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_WRITE,minphys,uio));
328 }
329 
330 /****************************************************************************/
331 /*                                 fdstart                                  */
332 /****************************************************************************/
333 fdstart(unit)
334 int unit;
335 {
336 	register struct buf *dp,*bp;
337 	int s;
338 
339 #ifdef FDTEST
340 printf("fd%d|",unit);
341 #endif
342 	s = splbio();
343 	if (!fd_unit[unit].motor) {
344 		fd_turnon(unit);
345 		/* Wait for 1 sec */
346 		timeout(fdstart,unit,hz);
347 		/*DELAY(1000000);*/
348 	}else
349 		 {
350 		/* make sure drive is selected as well as on */
351 		/*set_motor(unit,0);*/
352 
353 		dp = &fd_unit[unit].head;
354 		bp = dp->b_actf;
355 		fd_retry = 0;
356 		if (fd_unit[unit].reset) fd_state = 1;
357 		else {
358 			/* DO a RESET */
359 			fd_unit[unit].reset = 1;
360 			fd_state = 5;
361 		}
362 		fd_skip = 0;
363 #ifdef FDDEBUG
364 printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
365 #endif
366 		if (bp->b_cylin != fd_track) {
367 		/* Seek necessary, never quite sure where head is at! */
368 		out_fdc(15);	/* Seek function */
369 		out_fdc(unit);	/* Drive number */
370 		out_fdc(bp->b_cylin * dp->b_step);
371 		} else fdintr(0xff);
372 	}
373 	splx(s);
374 }
375 
376 fd_timeout(x)
377 int x;
378 {
379 	int i,j;
380 	struct buf *dp,*bp;
381 
382 	dp = &fd_unit[fd_drive].head;
383 	bp = dp->b_actf;
384 
385 	out_fdc(0x4);
386 	out_fdc(fd_drive);
387 	i = in_fdc();
388 	printf("Timeout drive status %lx\n",i);
389 
390 	out_fdc(0x8);
391 	i = in_fdc();
392 	j = in_fdc();
393 	printf("ST0 = %lx, PCN = %lx\n",i,j);
394 
395 	if (bp) badtrans(dp,bp);
396 }
397 
398 /****************************************************************************/
399 /*                                 fdintr                                   */
400 /****************************************************************************/
401 fdintr(unit)
402 {
403 	register struct buf *dp,*bp;
404 	struct buf *dpother;
405 	int read,head,trac,sec,i,s,sectrac,cyl;
406 	unsigned long blknum;
407 	struct fd_type *ft;
408 
409 #ifdef FDTEST
410 	printf("state %d, unit %d, dr %d|",fd_state,unit,fd_drive);
411 #endif
412 
413 	dp = &fd_unit[fd_drive].head;
414 	bp = dp->b_actf;
415 	read = bp->b_flags & B_READ;
416  	ft = &fd_types[FDTYPE(bp->b_dev)];
417 
418 	switch (fd_state) {
419 	case 1 : /* SEEK DONE, START DMA */
420 		/* Make sure seek really happened*/
421 		if (unit != 0xff) {
422 			out_fdc(0x8);
423 			i = in_fdc();
424 			cyl = in_fdc();
425 			if (!(i&0x20) || (cyl != bp->b_cylin*dp->b_step)) {
426 printf("Stray int ST0 = %lx, PCN = %lx:",i,cyl);
427 				return;
428 			}
429 		}
430 
431 		fd_track = bp->b_cylin;
432 		at_dma(read,bp->b_un.b_addr+fd_skip,FDBLK, fd_dmachan);
433 		blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
434 			+ fd_skip/FDBLK;
435 		sectrac = ft->sectrac;
436 		sec = blknum %  (sectrac * 2);
437 		head = sec / sectrac;
438 		sec = sec % sectrac + 1;
439 
440 		if (read)  out_fdc(0xE6);	/* READ */
441 		else out_fdc(0xC5);		/* WRITE */
442 		out_fdc(head << 2 | fd_drive);	/* head & unit */
443 		out_fdc(fd_track);		/* track */
444 		out_fdc(head);
445 		out_fdc(sec);			/* sector XXX +1? */
446 		out_fdc(ft->secsize);		/* sector size */
447 		out_fdc(sectrac);		/* sectors/track */
448 		out_fdc(ft->gap);		/* gap size */
449 		out_fdc(ft->datalen);		/* data length */
450 		fd_state = 2;
451 		/* XXX PARANOIA */
452 		untimeout(fd_timeout,2);
453 		timeout(fd_timeout,2,hz);
454 		break;
455 	case 2 : /* IO DONE, post-analyze */
456 		untimeout(fd_timeout,2);
457 		for(i=0;i<7;i++) {
458 			fd_status[i] = in_fdc();
459 		}
460 		if (fd_status[0]&0xF8) {
461 #ifdef FDOTHER
462 printf("status0 err %d:",fd_status[0]);
463 #endif
464 			goto retry;
465 		}
466 /*
467 		if (fd_status[1]){
468 			printf("status1 err %d:",fd_status[0]);
469 			goto retry;
470 		}
471 		if (fd_status[2]){
472 			printf("status2 err %d:",fd_status[0]);
473 			goto retry;
474 		}
475 */
476 		/* All OK */
477 		if (!kernel_space(bp->b_un.b_addr+fd_skip)) {
478 			/* RAW transfer */
479 			if (read) bcopy(dma_bounce[fd_dmachan]->b_un.b_addr,
480 				bp->b_un.b_addr+fd_skip, FDBLK);
481 		}
482 		fd_skip += FDBLK;
483 		if (fd_skip >= bp->b_bcount) {
484 #ifdef FDTEST
485 printf("DONE %d|", bp->b_blkno);
486 #endif
487 			/* ALL DONE */
488 			fd_skip = 0;
489 			bp->b_resid = 0;
490 			dp->b_actf = bp->av_forw;
491 			biodone(bp);
492 			nextstate(dp);
493 
494 		} else {
495 #ifdef FDDEBUG
496 printf("next|");
497 #endif
498 			/* set up next transfer */
499 			blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
500 				+ fd_skip/FDBLK;
501 			fd_state = 1;
502 			bp->b_cylin = (blknum / (ft->sectrac * 2));
503 			if (bp->b_cylin != fd_track) {
504 #ifdef FDTEST
505 printf("Seek|");
506 #endif
507 				/* SEEK Necessary */
508 				out_fdc(15);	/* Seek function */
509 				out_fdc(fd_drive);/* Drive number */
510 				out_fdc(bp->b_cylin * dp->b_step);
511 				break;
512 			} else fdintr(0xff);
513 		}
514 		break;
515 	case 3:
516 #ifdef FDOTHER
517 printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
518 #endif
519 		/* Seek necessary */
520 		out_fdc(15);	/* Seek function */
521 		out_fdc(fd_drive);/* Drive number */
522 		out_fdc(bp->b_cylin * dp->b_step);
523 		fd_state = 1;
524 		break;
525 	case 4:
526 		out_fdc(3); /* specify command */
527 		out_fdc(0xDF);
528 		out_fdc(2);
529 		out_fdc(7);	/* Recalibrate Function */
530 		out_fdc(fd_drive);
531 		fd_state = 3;
532 		break;
533 	case 5:
534 #ifdef FDOTHER
535 		printf("**RESET**\n");
536 #endif
537 		/* Try a reset, keep motor on */
538 		set_motor(fd_drive,1);
539 		set_motor(fd_drive,0);
540 		outb(fdc+fdctl,ft->trans);
541 		fd_retry++;
542 		fd_state = 4;
543 		break;
544 	default:
545 		printf("Unexpected FD int->");
546 		out_fdc(0x8);
547 		i = in_fdc();
548 		sec = in_fdc();
549 		printf("ST0 = %lx, PCN = %lx\n",i,sec);
550 		out_fdc(0x4A);
551 		out_fdc(fd_drive);
552 		for(i=0;i<7;i++) {
553 			fd_status[i] = in_fdc();
554 		}
555 	printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
556 		fd_status[0], fd_status[1], fd_status[2], fd_status[3],
557 		fd_status[4], fd_status[5], fd_status[6] );
558 		break;
559 	}
560 	return;
561 retry:
562 	switch(fd_retry) {
563 	case 0: case 1:
564 	case 2: case 3:
565 		break;
566 	case 4:
567 		fd_retry++;
568 		fd_state = 5;
569 		fdintr(0xff);
570 		return;
571 	case 5: case 6: case 7:
572 		break;
573 	default:
574 		printf("FD err %lx %lx %lx %lx %lx %lx %lx\n",
575 		fd_status[0], fd_status[1], fd_status[2], fd_status[3],
576 		fd_status[4], fd_status[5], fd_status[6] );
577 		badtrans(dp,bp);
578 		return;
579 	}
580 	fd_state = 1;
581 	fd_retry++;
582 	fdintr(0xff);
583 }
584 
585 badtrans(dp,bp)
586 struct buf *dp,*bp;
587 {
588 
589 	bp->b_flags |= B_ERROR;
590 	bp->b_error = EIO;
591 	bp->b_resid = bp->b_bcount - fd_skip;
592 	dp->b_actf = bp->av_forw;
593 	fd_skip = 0;
594 	biodone(bp);
595 	nextstate(dp);
596 
597 }
598 
599 /*
600 	nextstate : After a transfer is done, continue processing
601 	requests on the current drive queue.  If empty, go to
602 	the other drives queue.  If that is empty too, timeout
603 	to turn off the current drive in 5 seconds, and go
604 	to state 0 (not expecting any interrupts).
605 */
606 
607 nextstate(dp)
608 struct buf *dp;
609 {
610 
611 	if (dp->b_actf)
612 		fdstart(fd_drive);
613 	else {
614 #if NFD > 1
615 		struct buf *dpother;
616 
617 		dpother = &fd_unit[fd_drive ? 0 : 1].head;
618 
619 		if (dpother->b_actf) {
620 #ifdef FDTEST
621 printf("switch|");
622 #endif
623 			untimeout(fd_turnoff,fd_drive);
624 			timeout(fd_turnoff,fd_drive,5*hz);
625 			fd_drive = 1 - fd_drive;
626 			dp->b_active = 0;
627 			dpother->b_active = 1;
628 			fdstart(fd_drive);
629 		} else
630 #endif
631 		{
632 #ifdef FDTEST
633 printf("off|");
634 #endif
635 			untimeout(fd_turnoff,fd_drive);
636 			timeout(fd_turnoff,fd_drive,5*hz);
637 			fd_state = 0;
638 			dp->b_active = 0;
639 		}
640 	}
641 }
642 
643 Fdioctl() {}
644 Fddump() {}
645 #endif
646