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