atio.h -> isa.h, complete revision
[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 *
8 * %sccs.include.386.c%
9 *
10 * @(#)fd.c 5.1 (Berkeley) %G%
11 */
12
13/****************************************************************************/
14/* fd driver */
15/****************************************************************************/
16#include "param.h"
17#include "dkbad.h"
18#include "systm.h"
19#include "conf.h"
20#include "file.h"
21#include "dir.h"
22#include "user.h"
23#include "ioctl.h"
24#include "disk.h"
25#include "buf.h"
26#include "vm.h"
27#include "uio.h"
28#include "machine/pte.h"
29#include "machine/device.h"
30#include "icu.h"
31
32#define NFD 2
33#define FDUNIT(s) ((s)&1)
34#define FDTYPE(s) (((s)>>1)&7)
35#define b_cylin b_resid
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] = {
51 { 18,2,0xFF,0x1B,80,2880,1,0 }, /* 1.44 meg HD 3.5in floppy */
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 */
61 int opencnt; /* Num times open */
62 struct buf head; /* Head of buf chain */
63 struct buf rhead; /* Raw head of buf chain */
64} fd_unit[NFD];
65
66extern int hz;
67
68/* state needed for current transfer */
69static int fd_skip;
70static int fd_state;
71static int fd_retry;
72static int fd_drive;
73static int fd_status[7];
74static char fdrawbuf[FDBLK];
75
76/* stuff needed for virtual to physical calculations */
77extern char Sysbase;
78static unsigned long sbase = (unsigned long) &Sysbase;
79
80/****************************************************************************/
81/* autoconfiguration stuff */
82/****************************************************************************/
83int fdprobe(), fdattach(), fd_turnoff();
84
85struct driver fddriver = {
86 fdprobe, fdattach, "fd",
87};
88
89fdprobe(dev)
90struct device *dev;
91{
92 return 1;
93}
94
95fdattach(dev)
96struct device *dev;
97{
98 INTREN(IRQ6);
99}
100
101/****************************************************************************/
102/* fdstrategy */
103/****************************************************************************/
104fdstrategy(bp)
105 register struct buf *bp; /* IO operation to perform */
106{
107 register struct buf *dp,*dp0,*dp1;
108 long nblocks,blknum;
109 int unit, type, s;
110
111 unit = FDUNIT(minor(bp->b_dev));
112 type = FDTYPE(minor(bp->b_dev));
113#ifdef FDOTHER
114 printf("fdstrat: unit = %d, blkno = %d, bcount = %d\n",
115 unit, bp->b_blkno, bp->b_bcount);
116#endif
117 if ((unit >= NFD) || (bp->b_blkno < 0)) {
118 printf("fdstrat: unit = %d, blkno = %d, bcount = %d\n",
119 unit, bp->b_blkno, bp->b_bcount);
120 pg("fd:error in fdstrategy");
121 bp->b_error = EINVAL;
122 goto bad;
123 }
124 /*
125 * Set up block calculations.
126 */
127 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
128 nblocks = fd_types[type].size;
129 if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
130 if (blknum == nblocks) bp->b_resid = bp->b_bcount;
131 else bp->b_error = ENOSPC;
132 goto bad;
133 }
134 bp->b_cylin = blknum / (fd_types[type].sectrac * 2);
135 bp->b_cylin *= (fd_types[type].steptrac);
136 dp = &fd_unit[unit].head;
137 dp0 = &fd_unit[0].head;
138 dp1 = &fd_unit[1].head;
139 s = splbio();
140 disksort(dp, bp);
141 if ((dp0->b_active == 0)&&(dp1->b_active == 0)) {
142 dp->b_active = 1;
143 fd_drive = unit;
144 untimeout(fd_turnoff,unit);
145 fdstart(unit); /* start drive if idle */
146 }
147 splx(s);
148 return;
149
150bad:
151 bp->b_flags |= B_ERROR;
152 biodone(bp);
153}
154
155/****************************************************************************/
156/* motor control stuff */
157/****************************************************************************/
158set_motor(unit,reset)
159int unit,reset;
160{
161 int m0,m1;
162 m0 = fd_unit[0].motor;
163 m1 = fd_unit[1].motor;
164 outb(0x3f2,unit | (reset ? 0 : 0xC) | (m0 ? 16 : 0) | (m1 ? 32 : 0));
165}
166
167fd_turnoff(unit)
168int unit;
169{
170 fd_unit[unit].motor = 0;
171 if (unit) set_motor(0,0);
172 else set_motor(1,0);
173}
174
175fd_turnon(unit)
176int unit;
177{
178 fd_unit[unit].motor = 1;
179 set_motor(unit,0);
180}
181
182/****************************************************************************/
183/* fdc in/out */
184/****************************************************************************/
185int
186in_fdc()
187{
188 int i;
189 while ((i = inb(0x3f4) & 192) != 192) if (i == 128) return -1;
190 return inb(0x3f5);
191}
192
193dump_stat()
194{
195 int i;
196 for(i=0;i<7;i++) {
197 fd_status[i] = in_fdc();
198 if (fd_status[i] < 0) break;
199 }
200 printf("FD bad status :%X %X %X %X %X %X %X\n",
201 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
202 fd_status[4], fd_status[5], fd_status[6] );
203}
204
205out_fdc(x)
206int x;
207{
208 int r,errcnt;
209 static int maxcnt = 0;
210 errcnt = 0;
211 do {
212 r = (inb(0x3f4) & 192);
213 if (r==128) break;
214 if (r==192) {
215 dump_stat(); /* error: direction. eat up output */
216#ifdef FDOTHER
217 printf("%X\n",x);
218#endif
219 }
220 /* printf("Error r = %d:",r); */
221 errcnt++;
222 } while (1);
223 if (errcnt > maxcnt) {
224 maxcnt = errcnt;
225#ifdef FDOTHER
226 printf("New MAX = %d\n",maxcnt);
227#endif
228 }
229 outb(0x3f5,x&0xFF);
230}
231
232/* see if fdc responding */
233int
234check_fdc()
235{
236 int i;
237 for(i=0;i<100;i++) {
238 if (inb(0x3f4)&128) return 0;
239 }
240 return 1;
241}
242
243/****************************************************************************/
244/* fdopen/fdclose */
245/****************************************************************************/
246fdopen(dev, flags)
247 dev_t dev;
248 int flags;
249{
250 int unit = FDUNIT(minor(dev));
251 int type = FDTYPE(minor(dev));
252 int s;
253
254 /* check bounds */
255 if (unit >= NFD) return(ENXIO);
256 if (type >= NUMTYPES) return(ENXIO);
257 if (check_fdc()) return(EBUSY);
258
259 /* Set proper disk type, only allow one type */
260 s = splbio();
261 splx(s);
262
263 return 0;
264}
265
266fdclose(dev)
267 dev_t dev;
268{
269}
270
271/****************************************************************************/
272/* fdread/fdwrite */
273/****************************************************************************/
274/*
275 * Routines to do raw IO for a unit.
276 */
277fdread(dev, uio) /* character read routine */
278dev_t dev;
279struct uio *uio;
280{
281 int unit = FDUNIT(minor(dev)) ;
282 if (unit >= NFD) return(ENXIO);
283 return(physio(fdstrategy,&fd_unit[unit].rhead,dev,B_READ,minphys,uio));
284}
285
286fdwrite(dev, uio) /* character write routine */
287dev_t dev;
288struct uio *uio;
289{
290 int unit = FDUNIT(minor(dev));
291 if (unit >= NFD) return(ENXIO);
292 return(physio(fdstrategy,&fd_unit[unit].rhead,dev,B_WRITE,minphys,uio));
293}
294
295/****************************************************************************/
296/* fdstart */
297/****************************************************************************/
298fdstart(unit)
299int unit;
300{
301 register struct buf *dp,*bp;
302 int s;
303
304 if (!fd_unit[unit].motor) {
305 fd_turnon(unit);
306 /* Wait for 1 sec */
307 timeout(fdstart,unit,hz);
308 } else {
309 s = splbio();
310 dp = &fd_unit[unit].head;
311 bp = dp->b_actf;
312 fd_retry = 0;
313 fd_state = 1;
314 fd_skip = 0;
315 /* Seek necessary, never quite sure where head is at! */
316 out_fdc(15); /* Seek function */
317 out_fdc(unit); /* Drive number */
318 out_fdc(bp->b_cylin);
319 splx(s);
320 }
321}
322
323/* XXX temporary */
324kernel_space(x)
325unsigned long x;
326{
327 if ((x >= sbase) & (x < sbase + 0x800000)) return 1;
328 else return 0;
329}
330
331
332/****************************************************************************/
333/* fd_dma */
334/* set up DMA read/write operation and virtual address addr for nbytes */
335/****************************************************************************/
336fd_dma(read,addr,nbytes)
337int read;
338unsigned long addr;
339int nbytes;
340{
341 unsigned long phys;
342 int s,raw;
343
344 if (kernel_space(addr)) raw = 0;
345 else raw = 1;
346
347 /* copy bounce buffer on write */
348 if (raw && !read) bcopy(addr,fdrawbuf,FDBLK);
349
350 /* Set read/write bytes */
351 if (read) {
352 outb(0xC,0x46); outb(0xB,0x46);
353 } else {
354 outb(0xC,0x4A); outb(0xB,0x4A);
355 }
356 /* Send start address */
357 if (raw) phys = (unsigned long) &fdrawbuf[0];
358 else phys = addr;
359 /* translate to physical */
360 phys = phys - sbase;
361 outb(0x4,phys & 0xFF);
362 outb(0x4,(phys>>8) & 0xFF);
363 outb(0x81,(phys>>16) & 0xFF);
364 /* Send count */
365 nbytes--;
366 outb(0x5,nbytes & 0xFF);
367 outb(0x5,(nbytes>>8) & 0xFF);
368 /* set channel 2 */
369 outb(0x0A,2);
370}
371
372fd_timeout(x)
373int x;
374{
375 int i,j;
376 struct buf *dp,*bp;
377
378 dp = &fd_unit[fd_drive].head;
379 bp = dp->b_actf;
380
381 out_fdc(0x4);
382 out_fdc(fd_drive);
383 i = in_fdc();
384 printf("Timeout drive status %X\n",i);
385
386 out_fdc(0x8);
387 i = in_fdc();
388 j = in_fdc();
389 printf("ST0 = %X, PCN = %X\n",i,j);
390
391 if (bp) badtrans(dp,bp);
392}
393
394/****************************************************************************/
395/* fdintr */
396/****************************************************************************/
397fdintr(vec)
398int vec;
399{
400 register struct buf *dp,*bp;
401 struct buf *dpother;
402 int read,head,trac,sec,i,s,sectrac;
403 unsigned long blknum;
404 struct fd_type *ft;
405 static int fd_track;
406
407 dp = &fd_unit[fd_drive].head;
408 bp = dp->b_actf;
409 read = bp->b_flags & B_READ;
410 ft = &fd_types[FDTYPE(bp->b_dev)];
411
412 switch (fd_state) {
413 case 1 : /* SEEK DONE, START DMA */
414#ifdef FDOTHER
415 out_fdc(0x8);
416 i = in_fdc();
417 sec = in_fdc();
418 printf("ST0 = %X, PCN = %X:",i,sec);
419#endif
420 fd_track = bp->b_cylin;
421 fd_dma(read,bp->b_un.b_addr+fd_skip,FDBLK);
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 if (fd_status[1]){
456 printf("status1 err %d:",fd_status[0]);
457 goto retry;
458 }
459 if (fd_status[2]){
460 printf("status2 err %d:",fd_status[0]);
461 goto retry;
462 }
463 /* All OK */
464 if (!kernel_space(bp->b_un.b_addr+fd_skip)) {
465 /* RAW transfer */
466 if (read) bcopy(fdrawbuf,bp->b_un.b_addr+fd_skip,
467 DEV_BSIZE);
468 }
469 fd_skip += FDBLK;
470 if (fd_skip >= bp->b_bcount) {
471 /* ALL DONE */
472 fd_skip = 0;
473 bp->b_resid = 0;
474 dp->b_actf = bp->av_forw;
475 biodone(bp);
476 nextstate(dp);
477
478 } else {
479 /* set up next transfer */
480 blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
481 + fd_skip/FDBLK;
482 fd_state = 1;
483 bp->b_cylin = (blknum / (ft->sectrac * 2));
484 bp->b_cylin *= ft->steptrac;
485 if (bp->b_cylin != fd_track) {
486 /* SEEK Necessary */
487 out_fdc(15); /* Seek function */
488 out_fdc(fd_drive);/* Drive number */
489 out_fdc(bp->b_cylin);
490 break;
491 } else fdintr();
492 }
493 break;
494 case 3:
495 /* Seek necessary */
496 out_fdc(15); /* Seek function */
497 out_fdc(fd_drive);/* Drive number */
498 out_fdc(bp->b_cylin);
499 fd_state = 1;
500 break;
501 case 4:
502 out_fdc(3); /* specify command */
503 out_fdc(0xDF);
504 out_fdc(2);
505 out_fdc(7); /* Recalibrate Function */
506 out_fdc(fd_drive);
507 fd_state = 3;
508 break;
509 default:
510#ifdef FDDEBUG
511 printf("Unexpected FD int->");
512 out_fdc(0x8);
513 i = in_fdc();
514 sec = in_fdc();
515 printf("ST0 = %X, PCN = %X\n",i,sec);
516 out_fdc(0x4A);
517 out_fdc(fd_drive);
518 for(i=0;i<7;i++) {
519 fd_status[i] = in_fdc();
520 }
521 printf("intr status :%X %X %X %X %X %X %X ",
522 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
523 fd_status[4], fd_status[5], fd_status[6] );
524#endif
525 break;
526 }
527 return;
528retry:
529 switch(fd_retry) {
530 case 0: case 1:
531 break;
532 case 2:
533#ifdef FDDEBUG
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(0x3f7,ft->trans);
540 fd_retry++;
541 fd_state = 4;
542 return;
543 case 3: case 4:
544 case 5: case 6:
545 break;
546 default:
547 printf("FD err %X %X %X %X %X %X %X\n",
548 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
549 fd_status[4], fd_status[5], fd_status[6] );
550 badtrans(dp,bp);
551 return;
552 }
553 fd_state = 1;
554 fd_retry++;
555 fdintr();
556}
557
558badtrans(dp,bp)
559struct buf *dp,*bp;
560{
561
562 bp->b_flags |= B_ERROR;
563 bp->b_error = EIO;
564 bp->b_resid = bp->b_bcount - fd_skip;
565 dp->b_actf = bp->av_forw;
566 fd_skip = 0;
567 biodone(bp);
568 nextstate(dp);
569
570}
571
572/*
573 nextstate : After a transfer is done, continue processing
574 requests on the current drive queue. If empty, go to
575 the other drives queue. If that is empty too, timeout
576 to turn off the current drive in 5 seconds, and go
577 to state 0 (not expecting any interrupts).
578*/
579
580nextstate(dp)
581struct buf *dp;
582{
583 struct buf *dpother;
584
585 dpother = &fd_unit[fd_drive ? 0 : 1].head;
586 if (dp->b_actf) fdstart(fd_drive);
587 else if (dpother->b_actf) {
588 dp->b_active = 0;
589 fdstart(fd_drive ? 0 : 1);
590 } else {
591 untimeout(fd_turnoff,fd_drive);
592 timeout(fd_turnoff,fd_drive,5*hz);
593 fd_state = 0;
594 dp->b_active = 0;
595 }
596}