xref: /original-bsd/sys/i386/isa/fd.c (revision 7eb91141)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * 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	7.6 (Berkeley) 02/05/92
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 "param.h"
20 #include "dkbad.h"
21 #include "systm.h"
22 #include "conf.h"
23 #include "file.h"
24 #include "ioctl.h"
25 #include "buf.h"
26 #include "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 }
304 
305 /****************************************************************************/
306 /*                            fdread/fdwrite                                */
307 /****************************************************************************/
308 /*
309  * Routines to do raw IO for a unit.
310  */
311 Fdread(dev, uio)			/* character read routine */
312 dev_t dev;
313 struct uio *uio;
314 {
315  	int unit = FDUNIT(minor(dev)) ;
316 	if (unit >= NFD) return(ENXIO);
317 	return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_READ,minphys,uio));
318 }
319 
320 Fdwrite(dev, uio)			/* character write routine */
321 dev_t dev;
322 struct uio *uio;
323 {
324  	int unit = FDUNIT(minor(dev)) ;
325 	if (unit >= NFD) return(ENXIO);
326 	return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_WRITE,minphys,uio));
327 }
328 
329 /****************************************************************************/
330 /*                                 fdstart                                  */
331 /****************************************************************************/
332 fdstart(unit)
333 int unit;
334 {
335 	register struct buf *dp,*bp;
336 	int s;
337 
338 #ifdef FDTEST
339 printf("fd%d|",unit);
340 #endif
341 	s = splbio();
342 	if (!fd_unit[unit].motor) {
343 		fd_turnon(unit);
344 		/* Wait for 1 sec */
345 		timeout(fdstart,unit,hz);
346 		/*DELAY(1000000);*/
347 	}else
348 		 {
349 		/* make sure drive is selected as well as on */
350 		/*set_motor(unit,0);*/
351 
352 		dp = &fd_unit[unit].head;
353 		bp = dp->b_actf;
354 		fd_retry = 0;
355 		if (fd_unit[unit].reset) fd_state = 1;
356 		else {
357 			/* DO a RESET */
358 			fd_unit[unit].reset = 1;
359 			fd_state = 5;
360 		}
361 		fd_skip = 0;
362 #ifdef FDDEBUG
363 printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
364 #endif
365 		if (bp->b_cylin != fd_track) {
366 		/* Seek necessary, never quite sure where head is at! */
367 		out_fdc(15);	/* Seek function */
368 		out_fdc(unit);	/* Drive number */
369 		out_fdc(bp->b_cylin * dp->b_step);
370 		} else fdintr(0xff);
371 	}
372 	splx(s);
373 }
374 
375 fd_timeout(x)
376 int x;
377 {
378 	int i,j;
379 	struct buf *dp,*bp;
380 
381 	dp = &fd_unit[fd_drive].head;
382 	bp = dp->b_actf;
383 
384 	out_fdc(0x4);
385 	out_fdc(fd_drive);
386 	i = in_fdc();
387 	printf("Timeout drive status %lx\n",i);
388 
389 	out_fdc(0x8);
390 	i = in_fdc();
391 	j = in_fdc();
392 	printf("ST0 = %lx, PCN = %lx\n",i,j);
393 
394 	if (bp) badtrans(dp,bp);
395 }
396 
397 /****************************************************************************/
398 /*                                 fdintr                                   */
399 /****************************************************************************/
400 fdintr(unit)
401 {
402 	register struct buf *dp,*bp;
403 	struct buf *dpother;
404 	int read,head,trac,sec,i,s,sectrac,cyl;
405 	unsigned long blknum;
406 	struct fd_type *ft;
407 
408 #ifdef FDTEST
409 	printf("state %d, unit %d, dr %d|",fd_state,unit,fd_drive);
410 #endif
411 
412 	dp = &fd_unit[fd_drive].head;
413 	bp = dp->b_actf;
414 	read = bp->b_flags & B_READ;
415  	ft = &fd_types[FDTYPE(bp->b_dev)];
416 
417 	switch (fd_state) {
418 	case 1 : /* SEEK DONE, START DMA */
419 		/* Make sure seek really happened*/
420 		if (unit != 0xff) {
421 			out_fdc(0x8);
422 			i = in_fdc();
423 			cyl = in_fdc();
424 			if (!(i&0x20) || (cyl != bp->b_cylin*dp->b_step)) {
425 printf("Stray int ST0 = %lx, PCN = %lx:",i,cyl);
426 				return;
427 			}
428 		}
429 
430 		fd_track = bp->b_cylin;
431 		at_dma(read,bp->b_un.b_addr+fd_skip,FDBLK, fd_dmachan);
432 		blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
433 			+ fd_skip/FDBLK;
434 		sectrac = ft->sectrac;
435 		sec = blknum %  (sectrac * 2);
436 		head = sec / sectrac;
437 		sec = sec % sectrac + 1;
438 
439 		if (read)  out_fdc(0xE6);	/* READ */
440 		else out_fdc(0xC5);		/* WRITE */
441 		out_fdc(head << 2 | fd_drive);	/* head & unit */
442 		out_fdc(fd_track);		/* track */
443 		out_fdc(head);
444 		out_fdc(sec);			/* sector XXX +1? */
445 		out_fdc(ft->secsize);		/* sector size */
446 		out_fdc(sectrac);		/* sectors/track */
447 		out_fdc(ft->gap);		/* gap size */
448 		out_fdc(ft->datalen);		/* data length */
449 		fd_state = 2;
450 		/* XXX PARANOIA */
451 		untimeout(fd_timeout,2);
452 		timeout(fd_timeout,2,hz);
453 		break;
454 	case 2 : /* IO DONE, post-analyze */
455 		untimeout(fd_timeout,2);
456 		for(i=0;i<7;i++) {
457 			fd_status[i] = in_fdc();
458 		}
459 		if (fd_status[0]&0xF8) {
460 #ifdef FDOTHER
461 printf("status0 err %d:",fd_status[0]);
462 #endif
463 			goto retry;
464 		}
465 /*
466 		if (fd_status[1]){
467 			printf("status1 err %d:",fd_status[0]);
468 			goto retry;
469 		}
470 		if (fd_status[2]){
471 			printf("status2 err %d:",fd_status[0]);
472 			goto retry;
473 		}
474 */
475 		/* All OK */
476 		if (!kernel_space(bp->b_un.b_addr+fd_skip)) {
477 			/* RAW transfer */
478 			if (read) bcopy(dma_bounce[fd_dmachan]->b_un.b_addr,
479 				bp->b_un.b_addr+fd_skip, FDBLK);
480 		}
481 		fd_skip += FDBLK;
482 		if (fd_skip >= bp->b_bcount) {
483 #ifdef FDTEST
484 printf("DONE %d|", bp->b_blkno);
485 #endif
486 			/* ALL DONE */
487 			fd_skip = 0;
488 			bp->b_resid = 0;
489 			dp->b_actf = bp->av_forw;
490 			biodone(bp);
491 			nextstate(dp);
492 
493 		} else {
494 #ifdef FDDEBUG
495 printf("next|");
496 #endif
497 			/* set up next transfer */
498 			blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
499 				+ fd_skip/FDBLK;
500 			fd_state = 1;
501 			bp->b_cylin = (blknum / (ft->sectrac * 2));
502 			if (bp->b_cylin != fd_track) {
503 #ifdef FDTEST
504 printf("Seek|");
505 #endif
506 				/* SEEK Necessary */
507 				out_fdc(15);	/* Seek function */
508 				out_fdc(fd_drive);/* Drive number */
509 				out_fdc(bp->b_cylin * dp->b_step);
510 				break;
511 			} else fdintr(0xff);
512 		}
513 		break;
514 	case 3:
515 #ifdef FDOTHER
516 printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
517 #endif
518 		/* Seek necessary */
519 		out_fdc(15);	/* Seek function */
520 		out_fdc(fd_drive);/* Drive number */
521 		out_fdc(bp->b_cylin * dp->b_step);
522 		fd_state = 1;
523 		break;
524 	case 4:
525 		out_fdc(3); /* specify command */
526 		out_fdc(0xDF);
527 		out_fdc(2);
528 		out_fdc(7);	/* Recalibrate Function */
529 		out_fdc(fd_drive);
530 		fd_state = 3;
531 		break;
532 	case 5:
533 #ifdef FDOTHER
534 		printf("**RESET**\n");
535 #endif
536 		/* Try a reset, keep motor on */
537 		set_motor(fd_drive,1);
538 		set_motor(fd_drive,0);
539 		outb(fdc+fdctl,ft->trans);
540 		fd_retry++;
541 		fd_state = 4;
542 		break;
543 	default:
544 		printf("Unexpected FD int->");
545 		out_fdc(0x8);
546 		i = in_fdc();
547 		sec = in_fdc();
548 		printf("ST0 = %lx, PCN = %lx\n",i,sec);
549 		out_fdc(0x4A);
550 		out_fdc(fd_drive);
551 		for(i=0;i<7;i++) {
552 			fd_status[i] = in_fdc();
553 		}
554 	printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
555 		fd_status[0], fd_status[1], fd_status[2], fd_status[3],
556 		fd_status[4], fd_status[5], fd_status[6] );
557 		break;
558 	}
559 	return;
560 retry:
561 	switch(fd_retry) {
562 	case 0: case 1:
563 	case 2: case 3:
564 		break;
565 	case 4:
566 		fd_retry++;
567 		fd_state = 5;
568 		fdintr(0xff);
569 		return;
570 	case 5: case 6: case 7:
571 		break;
572 	default:
573 		printf("FD err %lx %lx %lx %lx %lx %lx %lx\n",
574 		fd_status[0], fd_status[1], fd_status[2], fd_status[3],
575 		fd_status[4], fd_status[5], fd_status[6] );
576 		badtrans(dp,bp);
577 		return;
578 	}
579 	fd_state = 1;
580 	fd_retry++;
581 	fdintr(0xff);
582 }
583 
584 badtrans(dp,bp)
585 struct buf *dp,*bp;
586 {
587 
588 	bp->b_flags |= B_ERROR;
589 	bp->b_error = EIO;
590 	bp->b_resid = bp->b_bcount - fd_skip;
591 	dp->b_actf = bp->av_forw;
592 	fd_skip = 0;
593 	biodone(bp);
594 	nextstate(dp);
595 
596 }
597 
598 /*
599 	nextstate : After a transfer is done, continue processing
600 	requests on the current drive queue.  If empty, go to
601 	the other drives queue.  If that is empty too, timeout
602 	to turn off the current drive in 5 seconds, and go
603 	to state 0 (not expecting any interrupts).
604 */
605 
606 nextstate(dp)
607 struct buf *dp;
608 {
609 
610 	if (dp->b_actf)
611 		fdstart(fd_drive);
612 	else {
613 #if NFD > 1
614 		struct buf *dpother;
615 
616 		dpother = &fd_unit[fd_drive ? 0 : 1].head;
617 
618 		if (dpother->b_actf) {
619 #ifdef FDTEST
620 printf("switch|");
621 #endif
622 			untimeout(fd_turnoff,fd_drive);
623 			timeout(fd_turnoff,fd_drive,5*hz);
624 			fd_drive = 1 - fd_drive;
625 			dp->b_active = 0;
626 			dpother->b_active = 1;
627 			fdstart(fd_drive);
628 		} else
629 #endif
630 		{
631 #ifdef FDTEST
632 printf("off|");
633 #endif
634 			untimeout(fd_turnoff,fd_drive);
635 			timeout(fd_turnoff,fd_drive,5*hz);
636 			fd_state = 0;
637 			dp->b_active = 0;
638 		}
639 	}
640 }
641 
642 Fdioctl() {}
643 Fddump() {}
644 #endif
645