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.6 (Berkeley) 02/05/92 11 */ 12 13 #include "fd.h" 14 #if NFD > 0 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 */ 19 #include "param.h" 20 #include "dkbad.h" 21 #include "systm.h" 22 #include "conf.h" 23 #include "file.h" 24 #include "ioctl.h" 25 #include "buf.h" 26 #include "uio.h" 27 28 #include "i386/isa/isa_device.h" 29 #include "i386/isa/fdreg.h" 30 #include "i386/isa/icu.h" 31 32 #define FDUNIT(s) ((s)&1) 33 #define FDTYPE(s) (((s)>>1)&7) 34 #define FDMOTOR(u) (fd_unit[(u)].motor ? (1 << (4 + (u))) : 0) 35 36 #define b_cylin b_resid 37 #define b_step b_resid 38 #define FDBLK 512 39 #define NUMTYPES 4 40 41 struct 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 52 struct fd_type fd_types[NUMTYPES] = { 53 { 18,2,0xFF,0x1B,80,2880,1,0 }, /* 1.44 meg HD 3.5in floppy */ 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 59 struct fd_u { 60 int type; /* Drive type (HD, DD */ 61 int active; /* Drive activity boolean */ 62 int motor; /* Motor on flag */ 63 struct buf head; /* Head of buf chain */ 64 struct buf rhead; /* Raw head of buf chain */ 65 int reset; 66 } fd_unit[NFD]; 67 68 69 extern int hz; 70 71 /* state needed for current transfer */ 72 static fdc; /* floppy disk controller io base register */ 73 int fd_dmachan = 2; 74 static int fd_skip; 75 static int fd_state; 76 static int fd_retry; 77 static int fd_drive; 78 static int fd_track = -1; 79 static int fd_status[7]; 80 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 */ 85 extern struct buf *dma_bounce[]; 86 87 /****************************************************************************/ 88 /* autoconfiguration stuff */ 89 /****************************************************************************/ 90 int fdprobe(), fdattach(), fd_turnoff(); 91 92 struct isa_driver fddriver = { 93 fdprobe, fdattach, "fd", 94 }; 95 96 fdprobe(dev) 97 struct isa_device *dev; 98 { 99 return 1; 100 } 101 102 fdattach(dev) 103 struct 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 112 int 113 Fdsize(dev) 114 dev_t dev; 115 { 116 return(2400); 117 } 118 119 /****************************************************************************/ 120 /* fdstrategy */ 121 /****************************************************************************/ 122 Fdstrategy(bp) 123 register struct buf *bp; /* IO operation to perform */ 124 { 125 register struct buf *dp; 126 long nblocks,blknum; 127 int unit, type, s; 128 129 unit = FDUNIT(minor(bp->b_dev)); 130 type = FDTYPE(minor(bp->b_dev)); 131 132 #ifdef FDTEST 133 printf("fdstrat%d, blk = %d, bcount = %d, addr = %x|", 134 unit, bp->b_blkno, bp->b_bcount,bp->b_un.b_addr); 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; 141 bp->b_flags |= B_ERROR; 142 goto bad; 143 } 144 /* 145 * Set up block calculations. 146 */ 147 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; 148 nblocks = fd_types[type].size; 149 if (blknum + (bp->b_bcount / FDBLK) > nblocks) { 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 } 156 #ifdef FDTEST 157 printf("fdstrat%d, too big\n"); 158 #endif 159 goto bad; 160 } 161 bp->b_cylin = blknum / (fd_types[type].sectrac * 2); 162 dp = &fd_unit[unit].head; 163 dp->b_step = (fd_types[fd_unit[unit].type].steptrac); 164 s = splbio(); 165 disksort(dp, bp); 166 if ((fd_unit[0].head.b_active == 0) 167 #if NFD > 1 168 && (fd_unit[1].head.b_active == 0) 169 #endif 170 ) { 171 #ifdef FDDEBUG 172 printf("T|"); 173 #endif 174 dp->b_active = 1; 175 fd_drive = unit; 176 fd_track = -1; /* force seek on first xfer */ 177 untimeout(fd_turnoff,unit); 178 fdstart(unit); /* start drive if idle */ 179 } 180 splx(s); 181 return; 182 183 bad: 184 biodone(bp); 185 } 186 187 /****************************************************************************/ 188 /* motor control stuff */ 189 /****************************************************************************/ 190 set_motor(unit,reset) 191 int unit,reset; 192 { 193 outb(fdc+fdout,unit | (reset ? 0 : 0xC) | FDMOTOR(0) 194 #if NFD > 1 195 | FDMOTOR(1) 196 #endif 197 ); 198 } 199 200 fd_turnoff(unit) 201 int unit; 202 { 203 fd_unit[unit].motor = 0; 204 if (unit) set_motor(0,0); 205 else set_motor(1,0); 206 } 207 208 fd_turnon(unit) 209 int unit; 210 { 211 fd_unit[unit].motor = 1; 212 set_motor(unit,0); 213 } 214 215 /****************************************************************************/ 216 /* fdc in/out */ 217 /****************************************************************************/ 218 int 219 in_fdc() 220 { 221 int i; 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); 225 } 226 227 dump_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 } 234 printf("FD bad status :%lx %lx %lx %lx %lx %lx %lx\n", 235 fd_status[0], fd_status[1], fd_status[2], fd_status[3], 236 fd_status[4], fd_status[5], fd_status[6] ); 237 } 238 239 out_fdc(x) 240 int x; 241 { 242 int r,errcnt; 243 static int maxcnt = 0; 244 errcnt = 0; 245 do { 246 r = (inb(fdc+fdsts) & (NE7_DIO|NE7_RQM)); 247 if (r== NE7_RQM) break; 248 if (r==(NE7_DIO|NE7_RQM)) { 249 dump_stat(); /* error: direction. eat up output */ 250 #ifdef FDOTHER 251 printf("%lx\n",x); 252 #endif 253 } 254 /* printf("Error r = %d:",r); */ 255 errcnt++; 256 } while (1); 257 if (errcnt > maxcnt) { 258 maxcnt = errcnt; 259 #ifdef FDOTHER 260 printf("New MAX = %d\n",maxcnt); 261 #endif 262 } 263 outb(fdc+fddata,x); 264 } 265 266 /* see if fdc responding */ 267 int 268 check_fdc() 269 { 270 int i; 271 for(i=0;i<100;i++) { 272 if (inb(fdc+fdsts)& NE7_RQM) return 0; 273 } 274 return 1; 275 } 276 277 /****************************************************************************/ 278 /* fdopen/fdclose */ 279 /****************************************************************************/ 280 Fdopen(dev, flags) 281 dev_t dev; 282 int flags; 283 { 284 int unit = FDUNIT(minor(dev)); 285 int type = FDTYPE(minor(dev)); 286 int s; 287 288 printf("fdopen %x %d %d\n", minor(dev), unit, type); 289 /* check bounds */ 290 if (unit >= NFD) return(ENXIO); 291 if (type >= NUMTYPES) return(ENXIO); 292 /* 293 if (check_fdc()) return(EBUSY); 294 */ 295 296 /* Set proper disk type, only allow one type */ 297 return 0; 298 } 299 300 Fdclose(dev, flags) 301 dev_t dev; 302 { 303 } 304 305 /****************************************************************************/ 306 /* fdread/fdwrite */ 307 /****************************************************************************/ 308 /* 309 * Routines to do raw IO for a unit. 310 */ 311 Fdread(dev, uio) /* character read routine */ 312 dev_t dev; 313 struct uio *uio; 314 { 315 int unit = FDUNIT(minor(dev)) ; 316 if (unit >= NFD) return(ENXIO); 317 return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_READ,minphys,uio)); 318 } 319 320 Fdwrite(dev, uio) /* character write routine */ 321 dev_t dev; 322 struct uio *uio; 323 { 324 int unit = FDUNIT(minor(dev)) ; 325 if (unit >= NFD) return(ENXIO); 326 return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_WRITE,minphys,uio)); 327 } 328 329 /****************************************************************************/ 330 /* fdstart */ 331 /****************************************************************************/ 332 fdstart(unit) 333 int unit; 334 { 335 register struct buf *dp,*bp; 336 int s; 337 338 #ifdef FDTEST 339 printf("fd%d|",unit); 340 #endif 341 s = splbio(); 342 if (!fd_unit[unit].motor) { 343 fd_turnon(unit); 344 /* Wait for 1 sec */ 345 timeout(fdstart,unit,hz); 346 /*DELAY(1000000);*/ 347 }else 348 { 349 /* make sure drive is selected as well as on */ 350 /*set_motor(unit,0);*/ 351 352 dp = &fd_unit[unit].head; 353 bp = dp->b_actf; 354 fd_retry = 0; 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 } 361 fd_skip = 0; 362 #ifdef FDDEBUG 363 printf("Seek %d %d\n", bp->b_cylin, dp->b_step); 364 #endif 365 if (bp->b_cylin != fd_track) { 366 /* Seek necessary, never quite sure where head is at! */ 367 out_fdc(15); /* Seek function */ 368 out_fdc(unit); /* Drive number */ 369 out_fdc(bp->b_cylin * dp->b_step); 370 } else fdintr(0xff); 371 } 372 splx(s); 373 } 374 375 fd_timeout(x) 376 int 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(); 387 printf("Timeout drive status %lx\n",i); 388 389 out_fdc(0x8); 390 i = in_fdc(); 391 j = in_fdc(); 392 printf("ST0 = %lx, PCN = %lx\n",i,j); 393 394 if (bp) badtrans(dp,bp); 395 } 396 397 /****************************************************************************/ 398 /* fdintr */ 399 /****************************************************************************/ 400 fdintr(unit) 401 { 402 register struct buf *dp,*bp; 403 struct buf *dpother; 404 int read,head,trac,sec,i,s,sectrac,cyl; 405 unsigned long blknum; 406 struct fd_type *ft; 407 408 #ifdef FDTEST 409 printf("state %d, unit %d, dr %d|",fd_state,unit,fd_drive); 410 #endif 411 412 dp = &fd_unit[fd_drive].head; 413 bp = dp->b_actf; 414 read = bp->b_flags & B_READ; 415 ft = &fd_types[FDTYPE(bp->b_dev)]; 416 417 switch (fd_state) { 418 case 1 : /* SEEK DONE, START DMA */ 419 /* Make sure seek really happened*/ 420 if (unit != 0xff) { 421 out_fdc(0x8); 422 i = in_fdc(); 423 cyl = in_fdc(); 424 if (!(i&0x20) || (cyl != bp->b_cylin*dp->b_step)) { 425 printf("Stray int ST0 = %lx, PCN = %lx:",i,cyl); 426 return; 427 } 428 } 429 430 fd_track = bp->b_cylin; 431 at_dma(read,bp->b_un.b_addr+fd_skip,FDBLK, fd_dmachan); 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 461 printf("status0 err %d:",fd_status[0]); 462 #endif 463 goto retry; 464 } 465 /* 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 } 474 */ 475 /* All OK */ 476 if (!kernel_space(bp->b_un.b_addr+fd_skip)) { 477 /* RAW transfer */ 478 if (read) bcopy(dma_bounce[fd_dmachan]->b_un.b_addr, 479 bp->b_un.b_addr+fd_skip, FDBLK); 480 } 481 fd_skip += FDBLK; 482 if (fd_skip >= bp->b_bcount) { 483 #ifdef FDTEST 484 printf("DONE %d|", bp->b_blkno); 485 #endif 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 { 494 #ifdef FDDEBUG 495 printf("next|"); 496 #endif 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)); 502 if (bp->b_cylin != fd_track) { 503 #ifdef FDTEST 504 printf("Seek|"); 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 break; 511 } else fdintr(0xff); 512 } 513 break; 514 case 3: 515 #ifdef FDOTHER 516 printf("Seek %d %d\n", bp->b_cylin, dp->b_step); 517 #endif 518 /* Seek necessary */ 519 out_fdc(15); /* Seek function */ 520 out_fdc(fd_drive);/* Drive number */ 521 out_fdc(bp->b_cylin * dp->b_step); 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; 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; 543 default: 544 printf("Unexpected FD int->"); 545 out_fdc(0x8); 546 i = in_fdc(); 547 sec = in_fdc(); 548 printf("ST0 = %lx, PCN = %lx\n",i,sec); 549 out_fdc(0x4A); 550 out_fdc(fd_drive); 551 for(i=0;i<7;i++) { 552 fd_status[i] = in_fdc(); 553 } 554 printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", 555 fd_status[0], fd_status[1], fd_status[2], fd_status[3], 556 fd_status[4], fd_status[5], fd_status[6] ); 557 break; 558 } 559 return; 560 retry: 561 switch(fd_retry) { 562 case 0: case 1: 563 case 2: case 3: 564 break; 565 case 4: 566 fd_retry++; 567 fd_state = 5; 568 fdintr(0xff); 569 return; 570 case 5: case 6: case 7: 571 break; 572 default: 573 printf("FD err %lx %lx %lx %lx %lx %lx %lx\n", 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++; 581 fdintr(0xff); 582 } 583 584 badtrans(dp,bp) 585 struct 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 606 nextstate(dp) 607 struct buf *dp; 608 { 609 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) { 619 #ifdef FDTEST 620 printf("switch|"); 621 #endif 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 { 631 #ifdef FDTEST 632 printf("off|"); 633 #endif 634 untimeout(fd_turnoff,fd_drive); 635 timeout(fd_turnoff,fd_drive,5*hz); 636 fd_state = 0; 637 dp->b_active = 0; 638 } 639 } 640 } 641 642 Fdioctl() {} 643 Fddump() {} 644 #endif 645