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