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