added ISO network software interrupts
[unix-history] / usr / src / sys / i386 / isa / fd.c
CommitLineData
7ff33365
DA
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 *
6808e4e6 8 * %sccs.include.redist.c%
7ff33365 9 *
d7070096 10 * @(#)fd.c 7.4 (Berkeley) %G%
7ff33365
DA
11 */
12
d91e594a
KB
13#include "fd.h"
14#if NFD > 0
15
7ff33365
DA
16#include "param.h"
17#include "dkbad.h"
18#include "systm.h"
19#include "conf.h"
20#include "file.h"
7ff33365 21#include "ioctl.h"
7ff33365 22#include "buf.h"
7ff33365 23#include "uio.h"
8dfab1b8
WN
24#include "i386/isa/isa_device.h"
25#include "i386/isa/fdreg.h"
26#include "i386/isa/icu.h"
7ff33365 27
7ff33365
DA
28#define FDUNIT(s) ((s)&1)
29#define FDTYPE(s) (((s)>>1)&7)
32f56e31 30
7ff33365 31#define b_cylin b_resid
32f56e31 32#define b_step b_resid
7ff33365
DA
33#define FDBLK 512
34#define NUMTYPES 4
35
36struct 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
47struct fd_type fd_types[NUMTYPES] = {
32f56e31 48 { 18,2,0xFF,0x1B,80,2880,1,0 }, /* 1.44 meg HD 3.5in floppy */
7ff33365
DA
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
54struct fd_u {
55 int type; /* Drive type (HD, DD */
56 int active; /* Drive activity boolean */
57 int motor; /* Motor on flag */
7ff33365
DA
58 struct buf head; /* Head of buf chain */
59 struct buf rhead; /* Raw head of buf chain */
32f56e31 60 int reset;
7ff33365
DA
61} fd_unit[NFD];
62
32f56e31 63
7ff33365
DA
64extern int hz;
65
66/* state needed for current transfer */
32f56e31
BJ
67static fdc; /* floppy disk controller io base register */
68int fd_dmachan = 2;
7ff33365
DA
69static int fd_skip;
70static int fd_state;
71static int fd_retry;
72static int fd_drive;
32f56e31 73static int fd_track = -1;
7ff33365 74static int fd_status[7];
7ff33365 75
32f56e31
BJ
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*/
80extern struct buf *dma_bounce[];
7ff33365
DA
81
82/****************************************************************************/
83/* autoconfiguration stuff */
84/****************************************************************************/
85int fdprobe(), fdattach(), fd_turnoff();
86
32f56e31 87struct isa_driver fddriver = {
7ff33365
DA
88 fdprobe, fdattach, "fd",
89};
90
91fdprobe(dev)
32f56e31 92struct isa_device *dev;
7ff33365
DA
93{
94 return 1;
95}
96
97fdattach(dev)
32f56e31
BJ
98struct 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
107int
108fdsize(dev)
109dev_t dev;
7ff33365 110{
32f56e31 111 return(2400);
7ff33365
DA
112}
113
114/****************************************************************************/
115/* fdstrategy */
116/****************************************************************************/
117fdstrategy(bp)
118 register struct buf *bp; /* IO operation to perform */
119{
120 register struct buf *dp,*dp0,*dp1;
121 long nblocks,blknum;
32f56e31 122 int unit, type, s;
7ff33365 123
32f56e31
BJ
124 unit = FDUNIT(minor(bp->b_dev));
125 type = FDTYPE(minor(bp->b_dev));
126
127#ifdef FDTEST
128printf("fdstrat%d, blk = %d, bcount = %d, addr = %x|",
129 unit, bp->b_blkno, bp->b_bcount,bp->b_un.b_addr);
7ff33365
DA
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;
32f56e31 136 bp->b_flags |= B_ERROR;
7ff33365
DA
137 goto bad;
138 }
139 /*
140 * Set up block calculations.
141 */
142 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
32f56e31 143 nblocks = fd_types[type].size;
7ff33365 144 if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
32f56e31
BJ
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 }
7ff33365
DA
151 goto bad;
152 }
32f56e31 153 bp->b_cylin = blknum / (fd_types[type].sectrac * 2);
7ff33365
DA
154 dp = &fd_unit[unit].head;
155 dp0 = &fd_unit[0].head;
156 dp1 = &fd_unit[1].head;
32f56e31 157 dp->b_step = (fd_types[fd_unit[unit].type].steptrac);
7ff33365
DA
158 s = splbio();
159 disksort(dp, bp);
160 if ((dp0->b_active == 0)&&(dp1->b_active == 0)) {
32f56e31
BJ
161#ifdef FDDEBUG
162printf("T|");
163#endif
7ff33365
DA
164 dp->b_active = 1;
165 fd_drive = unit;
32f56e31 166 fd_track = -1; /* force seek on first xfer */
7ff33365
DA
167 untimeout(fd_turnoff,unit);
168 fdstart(unit); /* start drive if idle */
169 }
170 splx(s);
171 return;
172
173bad:
7ff33365
DA
174 biodone(bp);
175}
176
177/****************************************************************************/
178/* motor control stuff */
179/****************************************************************************/
180set_motor(unit,reset)
181int unit,reset;
182{
183 int m0,m1;
184 m0 = fd_unit[0].motor;
185 m1 = fd_unit[1].motor;
32f56e31 186 outb(fdc+fdout,unit | (reset ? 0 : 0xC) | (m0 ? 16 : 0) | (m1 ? 32 : 0));
7ff33365
DA
187}
188
189fd_turnoff(unit)
190int unit;
191{
192 fd_unit[unit].motor = 0;
193 if (unit) set_motor(0,0);
194 else set_motor(1,0);
195}
196
197fd_turnon(unit)
198int unit;
199{
200 fd_unit[unit].motor = 1;
201 set_motor(unit,0);
202}
203
204/****************************************************************************/
205/* fdc in/out */
206/****************************************************************************/
207int
208in_fdc()
209{
210 int i;
32f56e31
BJ
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);
7ff33365
DA
214}
215
216dump_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 }
d7070096 223printf("FD bad status :%lx %lx %lx %lx %lx %lx %lx\n",
32f56e31
BJ
224 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
225 fd_status[4], fd_status[5], fd_status[6] );
7ff33365
DA
226}
227
228out_fdc(x)
229int x;
230{
231 int r,errcnt;
232 static int maxcnt = 0;
233 errcnt = 0;
234 do {
32f56e31
BJ
235 r = (inb(fdc+fdsts) & (NE7_DIO|NE7_RQM));
236 if (r== NE7_RQM) break;
237 if (r==(NE7_DIO|NE7_RQM)) {
7ff33365
DA
238 dump_stat(); /* error: direction. eat up output */
239#ifdef FDOTHER
d7070096 240printf("%lx\n",x);
7ff33365
DA
241#endif
242 }
243 /* printf("Error r = %d:",r); */
244 errcnt++;
245 } while (1);
246 if (errcnt > maxcnt) {
247 maxcnt = errcnt;
248#ifdef FDOTHER
32f56e31 249printf("New MAX = %d\n",maxcnt);
7ff33365
DA
250#endif
251 }
32f56e31 252 outb(fdc+fddata,x);
7ff33365
DA
253}
254
255/* see if fdc responding */
256int
257check_fdc()
258{
259 int i;
260 for(i=0;i<100;i++) {
32f56e31 261 if (inb(fdc+fdsts)& NE7_RQM) return 0;
7ff33365
DA
262 }
263 return 1;
264}
265
266/****************************************************************************/
267/* fdopen/fdclose */
268/****************************************************************************/
8dfab1b8 269Fdopen(dev, flags)
7ff33365
DA
270 dev_t dev;
271 int flags;
272{
32f56e31
BJ
273 int unit = FDUNIT(minor(dev));
274 int type = FDTYPE(minor(dev));
7ff33365
DA
275 int s;
276
277 /* check bounds */
278 if (unit >= NFD) return(ENXIO);
279 if (type >= NUMTYPES) return(ENXIO);
32f56e31 280/*
7ff33365 281 if (check_fdc()) return(EBUSY);
32f56e31 282*/
7ff33365
DA
283
284 /* Set proper disk type, only allow one type */
7ff33365
DA
285 return 0;
286}
287
8dfab1b8 288fdclose(dev, flags)
7ff33365
DA
289 dev_t dev;
290{
291}
292
293/****************************************************************************/
294/* fdread/fdwrite */
295/****************************************************************************/
296/*
297 * Routines to do raw IO for a unit.
298 */
299fdread(dev, uio) /* character read routine */
300dev_t dev;
301struct uio *uio;
302{
32f56e31 303 int unit = FDUNIT(minor(dev)) ;
7ff33365
DA
304 if (unit >= NFD) return(ENXIO);
305 return(physio(fdstrategy,&fd_unit[unit].rhead,dev,B_READ,minphys,uio));
306}
307
308fdwrite(dev, uio) /* character write routine */
309dev_t dev;
310struct uio *uio;
311{
32f56e31 312 int unit = FDUNIT(minor(dev)) ;
7ff33365
DA
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/****************************************************************************/
320fdstart(unit)
321int unit;
322{
323 register struct buf *dp,*bp;
324 int s;
325
32f56e31
BJ
326#ifdef FDTEST
327printf("st%d|",unit);
328#endif
329 s = splbio();
7ff33365
DA
330 if (!fd_unit[unit].motor) {
331 fd_turnon(unit);
332 /* Wait for 1 sec */
333 timeout(fdstart,unit,hz);
32f56e31
BJ
334 /*DELAY(1000000);*/
335 }else
336 {
337 /* make sure drive is selected as well as on */
338 /*set_motor(unit,0);*/
339
7ff33365
DA
340 dp = &fd_unit[unit].head;
341 bp = dp->b_actf;
342 fd_retry = 0;
32f56e31
BJ
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 }
7ff33365 349 fd_skip = 0;
32f56e31
BJ
350#ifdef FDDEBUG
351printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
352#endif
353 if (bp->b_cylin != fd_track) {
7ff33365
DA
354 /* Seek necessary, never quite sure where head is at! */
355 out_fdc(15); /* Seek function */
356 out_fdc(unit); /* Drive number */
32f56e31 357 out_fdc(bp->b_cylin * dp->b_step);
8dfab1b8 358 } else fdintr(0xff);
7ff33365 359 }
32f56e31 360 splx(s);
7ff33365
DA
361}
362
363fd_timeout(x)
364int 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();
d7070096 375 printf("Timeout drive status %lx\n",i);
7ff33365
DA
376
377 out_fdc(0x8);
378 i = in_fdc();
379 j = in_fdc();
d7070096 380 printf("ST0 = %lx, PCN = %lx\n",i,j);
7ff33365
DA
381
382 if (bp) badtrans(dp,bp);
383}
384
385/****************************************************************************/
386/* fdintr */
387/****************************************************************************/
8dfab1b8 388fdintr(unit)
7ff33365
DA
389{
390 register struct buf *dp,*bp;
391 struct buf *dpother;
32f56e31 392 int read,head,trac,sec,i,s,sectrac,cyl;
7ff33365
DA
393 unsigned long blknum;
394 struct fd_type *ft;
32f56e31
BJ
395
396#ifdef FDTEST
8dfab1b8 397 printf("state %d, unit %d, dr %d|",fd_state,unit,fd_drive);
32f56e31 398#endif
7ff33365
DA
399
400 dp = &fd_unit[fd_drive].head;
401 bp = dp->b_actf;
402 read = bp->b_flags & B_READ;
32f56e31 403 ft = &fd_types[FDTYPE(bp->b_dev)];
7ff33365
DA
404
405 switch (fd_state) {
406 case 1 : /* SEEK DONE, START DMA */
32f56e31 407 /* Make sure seek really happened*/
8dfab1b8 408 if (unit != 0xff) {
32f56e31
BJ
409 out_fdc(0x8);
410 i = in_fdc();
411 cyl = in_fdc();
412 if (!(i&0x20) || (cyl != bp->b_cylin*dp->b_step)) {
d7070096 413printf("Stray int ST0 = %lx, PCN = %lx:",i,cyl);
32f56e31
BJ
414 return;
415 }
416 }
417
7ff33365 418 fd_track = bp->b_cylin;
32f56e31 419 at_dma(read,bp->b_un.b_addr+fd_skip,FDBLK, fd_dmachan);
7ff33365
DA
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
32f56e31 449printf("status0 err %d:",fd_status[0]);
7ff33365
DA
450#endif
451 goto retry;
452 }
32f56e31 453/*
7ff33365
DA
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 }
32f56e31 462*/
7ff33365
DA
463 /* All OK */
464 if (!kernel_space(bp->b_un.b_addr+fd_skip)) {
465 /* RAW transfer */
32f56e31
BJ
466 if (read) bcopy(dma_bounce[fd_dmachan]->b_un.b_addr,
467 bp->b_un.b_addr+fd_skip, FDBLK);
7ff33365
DA
468 }
469 fd_skip += FDBLK;
470 if (fd_skip >= bp->b_bcount) {
32f56e31
BJ
471#ifdef FDTEST
472printf("DONE %d|", bp->b_blkno);
473#endif
7ff33365
DA
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 {
32f56e31
BJ
482#ifdef FDDEBUG
483printf("next|");
484#endif
7ff33365
DA
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));
7ff33365 490 if (bp->b_cylin != fd_track) {
32f56e31
BJ
491#ifdef FDTEST
492printf("Seek|");
493#endif
7ff33365
DA
494 /* SEEK Necessary */
495 out_fdc(15); /* Seek function */
496 out_fdc(fd_drive);/* Drive number */
32f56e31 497 out_fdc(bp->b_cylin * dp->b_step);
7ff33365 498 break;
8dfab1b8 499 } else fdintr(0xff);
7ff33365
DA
500 }
501 break;
502 case 3:
32f56e31
BJ
503#ifdef FDOTHER
504printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
505#endif
7ff33365
DA
506 /* Seek necessary */
507 out_fdc(15); /* Seek function */
508 out_fdc(fd_drive);/* Drive number */
32f56e31 509 out_fdc(bp->b_cylin * dp->b_step);
7ff33365
DA
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;
32f56e31
BJ
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;
7ff33365 531 default:
7ff33365
DA
532 printf("Unexpected FD int->");
533 out_fdc(0x8);
534 i = in_fdc();
535 sec = in_fdc();
d7070096 536 printf("ST0 = %lx, PCN = %lx\n",i,sec);
7ff33365
DA
537 out_fdc(0x4A);
538 out_fdc(fd_drive);
539 for(i=0;i<7;i++) {
540 fd_status[i] = in_fdc();
541 }
d7070096 542 printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
7ff33365
DA
543 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
544 fd_status[4], fd_status[5], fd_status[6] );
7ff33365
DA
545 break;
546 }
547 return;
548retry:
549 switch(fd_retry) {
550 case 0: case 1:
32f56e31 551 case 2: case 3:
7ff33365 552 break;
32f56e31 553 case 4:
7ff33365 554 fd_retry++;
32f56e31 555 fd_state = 5;
8dfab1b8 556 fdintr(0xff);
7ff33365 557 return;
32f56e31 558 case 5: case 6: case 7:
7ff33365
DA
559 break;
560 default:
d7070096 561 printf("FD err %lx %lx %lx %lx %lx %lx %lx\n",
7ff33365
DA
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++;
8dfab1b8 569 fdintr(0xff);
7ff33365
DA
570}
571
572badtrans(dp,bp)
573struct 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
594nextstate(dp)
595struct 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) {
32f56e31
BJ
602#ifdef FDTEST
603printf("switch|");
604#endif
605 untimeout(fd_turnoff,fd_drive);
606 timeout(fd_turnoff,fd_drive,5*hz);
607 fd_drive = 1 - fd_drive;
7ff33365 608 dp->b_active = 0;
32f56e31
BJ
609 dpother->b_active = 1;
610 fdstart(fd_drive);
7ff33365 611 } else {
32f56e31
BJ
612#ifdef FDTEST
613printf("off|");
614#endif
7ff33365
DA
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}
32f56e31 621#endif