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