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