1 /*- 2 * Copyright (c) 1990, 1993 3 * The Regents of the University of California. 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 8.1 (Berkeley) 06/11/93 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 <sys/param.h> 20 #include <sys/dkbad.h> 21 #include <sys/systm.h> 22 #include <sys/conf.h> 23 #include <sys/file.h> 24 #include <sys/ioctl.h> 25 #include <sys/buf.h> 26 #include <sys/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 return (0); 304 } 305 306 /****************************************************************************/ 307 /* fdread/fdwrite */ 308 /****************************************************************************/ 309 /* 310 * Routines to do raw IO for a unit. 311 */ 312 Fdread(dev, uio) /* character read routine */ 313 dev_t dev; 314 struct uio *uio; 315 { 316 int unit = FDUNIT(minor(dev)) ; 317 if (unit >= NFD) return(ENXIO); 318 return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_READ,minphys,uio)); 319 } 320 321 Fdwrite(dev, uio) /* character write routine */ 322 dev_t dev; 323 struct uio *uio; 324 { 325 int unit = FDUNIT(minor(dev)) ; 326 if (unit >= NFD) return(ENXIO); 327 return(physio(Fdstrategy,&fd_unit[unit].rhead,dev,B_WRITE,minphys,uio)); 328 } 329 330 /****************************************************************************/ 331 /* fdstart */ 332 /****************************************************************************/ 333 fdstart(unit) 334 int unit; 335 { 336 register struct buf *dp,*bp; 337 int s; 338 339 #ifdef FDTEST 340 printf("fd%d|",unit); 341 #endif 342 s = splbio(); 343 if (!fd_unit[unit].motor) { 344 fd_turnon(unit); 345 /* Wait for 1 sec */ 346 timeout(fdstart,unit,hz); 347 /*DELAY(1000000);*/ 348 }else 349 { 350 /* make sure drive is selected as well as on */ 351 /*set_motor(unit,0);*/ 352 353 dp = &fd_unit[unit].head; 354 bp = dp->b_actf; 355 fd_retry = 0; 356 if (fd_unit[unit].reset) fd_state = 1; 357 else { 358 /* DO a RESET */ 359 fd_unit[unit].reset = 1; 360 fd_state = 5; 361 } 362 fd_skip = 0; 363 #ifdef FDDEBUG 364 printf("Seek %d %d\n", bp->b_cylin, dp->b_step); 365 #endif 366 if (bp->b_cylin != fd_track) { 367 /* Seek necessary, never quite sure where head is at! */ 368 out_fdc(15); /* Seek function */ 369 out_fdc(unit); /* Drive number */ 370 out_fdc(bp->b_cylin * dp->b_step); 371 } else fdintr(0xff); 372 } 373 splx(s); 374 } 375 376 fd_timeout(x) 377 int x; 378 { 379 int i,j; 380 struct buf *dp,*bp; 381 382 dp = &fd_unit[fd_drive].head; 383 bp = dp->b_actf; 384 385 out_fdc(0x4); 386 out_fdc(fd_drive); 387 i = in_fdc(); 388 printf("Timeout drive status %lx\n",i); 389 390 out_fdc(0x8); 391 i = in_fdc(); 392 j = in_fdc(); 393 printf("ST0 = %lx, PCN = %lx\n",i,j); 394 395 if (bp) badtrans(dp,bp); 396 } 397 398 /****************************************************************************/ 399 /* fdintr */ 400 /****************************************************************************/ 401 fdintr(unit) 402 { 403 register struct buf *dp,*bp; 404 struct buf *dpother; 405 int read,head,trac,sec,i,s,sectrac,cyl; 406 unsigned long blknum; 407 struct fd_type *ft; 408 409 #ifdef FDTEST 410 printf("state %d, unit %d, dr %d|",fd_state,unit,fd_drive); 411 #endif 412 413 dp = &fd_unit[fd_drive].head; 414 bp = dp->b_actf; 415 read = bp->b_flags & B_READ; 416 ft = &fd_types[FDTYPE(bp->b_dev)]; 417 418 switch (fd_state) { 419 case 1 : /* SEEK DONE, START DMA */ 420 /* Make sure seek really happened*/ 421 if (unit != 0xff) { 422 out_fdc(0x8); 423 i = in_fdc(); 424 cyl = in_fdc(); 425 if (!(i&0x20) || (cyl != bp->b_cylin*dp->b_step)) { 426 printf("Stray int ST0 = %lx, PCN = %lx:",i,cyl); 427 return; 428 } 429 } 430 431 fd_track = bp->b_cylin; 432 at_dma(read,bp->b_un.b_addr+fd_skip,FDBLK, fd_dmachan); 433 blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK 434 + fd_skip/FDBLK; 435 sectrac = ft->sectrac; 436 sec = blknum % (sectrac * 2); 437 head = sec / sectrac; 438 sec = sec % sectrac + 1; 439 440 if (read) out_fdc(0xE6); /* READ */ 441 else out_fdc(0xC5); /* WRITE */ 442 out_fdc(head << 2 | fd_drive); /* head & unit */ 443 out_fdc(fd_track); /* track */ 444 out_fdc(head); 445 out_fdc(sec); /* sector XXX +1? */ 446 out_fdc(ft->secsize); /* sector size */ 447 out_fdc(sectrac); /* sectors/track */ 448 out_fdc(ft->gap); /* gap size */ 449 out_fdc(ft->datalen); /* data length */ 450 fd_state = 2; 451 /* XXX PARANOIA */ 452 untimeout(fd_timeout,2); 453 timeout(fd_timeout,2,hz); 454 break; 455 case 2 : /* IO DONE, post-analyze */ 456 untimeout(fd_timeout,2); 457 for(i=0;i<7;i++) { 458 fd_status[i] = in_fdc(); 459 } 460 if (fd_status[0]&0xF8) { 461 #ifdef FDOTHER 462 printf("status0 err %d:",fd_status[0]); 463 #endif 464 goto retry; 465 } 466 /* 467 if (fd_status[1]){ 468 printf("status1 err %d:",fd_status[0]); 469 goto retry; 470 } 471 if (fd_status[2]){ 472 printf("status2 err %d:",fd_status[0]); 473 goto retry; 474 } 475 */ 476 /* All OK */ 477 if (!kernel_space(bp->b_un.b_addr+fd_skip)) { 478 /* RAW transfer */ 479 if (read) bcopy(dma_bounce[fd_dmachan]->b_un.b_addr, 480 bp->b_un.b_addr+fd_skip, FDBLK); 481 } 482 fd_skip += FDBLK; 483 if (fd_skip >= bp->b_bcount) { 484 #ifdef FDTEST 485 printf("DONE %d|", bp->b_blkno); 486 #endif 487 /* ALL DONE */ 488 fd_skip = 0; 489 bp->b_resid = 0; 490 dp->b_actf = bp->av_forw; 491 biodone(bp); 492 nextstate(dp); 493 494 } else { 495 #ifdef FDDEBUG 496 printf("next|"); 497 #endif 498 /* set up next transfer */ 499 blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK 500 + fd_skip/FDBLK; 501 fd_state = 1; 502 bp->b_cylin = (blknum / (ft->sectrac * 2)); 503 if (bp->b_cylin != fd_track) { 504 #ifdef FDTEST 505 printf("Seek|"); 506 #endif 507 /* SEEK Necessary */ 508 out_fdc(15); /* Seek function */ 509 out_fdc(fd_drive);/* Drive number */ 510 out_fdc(bp->b_cylin * dp->b_step); 511 break; 512 } else fdintr(0xff); 513 } 514 break; 515 case 3: 516 #ifdef FDOTHER 517 printf("Seek %d %d\n", bp->b_cylin, dp->b_step); 518 #endif 519 /* Seek necessary */ 520 out_fdc(15); /* Seek function */ 521 out_fdc(fd_drive);/* Drive number */ 522 out_fdc(bp->b_cylin * dp->b_step); 523 fd_state = 1; 524 break; 525 case 4: 526 out_fdc(3); /* specify command */ 527 out_fdc(0xDF); 528 out_fdc(2); 529 out_fdc(7); /* Recalibrate Function */ 530 out_fdc(fd_drive); 531 fd_state = 3; 532 break; 533 case 5: 534 #ifdef FDOTHER 535 printf("**RESET**\n"); 536 #endif 537 /* Try a reset, keep motor on */ 538 set_motor(fd_drive,1); 539 set_motor(fd_drive,0); 540 outb(fdc+fdctl,ft->trans); 541 fd_retry++; 542 fd_state = 4; 543 break; 544 default: 545 printf("Unexpected FD int->"); 546 out_fdc(0x8); 547 i = in_fdc(); 548 sec = in_fdc(); 549 printf("ST0 = %lx, PCN = %lx\n",i,sec); 550 out_fdc(0x4A); 551 out_fdc(fd_drive); 552 for(i=0;i<7;i++) { 553 fd_status[i] = in_fdc(); 554 } 555 printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", 556 fd_status[0], fd_status[1], fd_status[2], fd_status[3], 557 fd_status[4], fd_status[5], fd_status[6] ); 558 break; 559 } 560 return; 561 retry: 562 switch(fd_retry) { 563 case 0: case 1: 564 case 2: case 3: 565 break; 566 case 4: 567 fd_retry++; 568 fd_state = 5; 569 fdintr(0xff); 570 return; 571 case 5: case 6: case 7: 572 break; 573 default: 574 printf("FD err %lx %lx %lx %lx %lx %lx %lx\n", 575 fd_status[0], fd_status[1], fd_status[2], fd_status[3], 576 fd_status[4], fd_status[5], fd_status[6] ); 577 badtrans(dp,bp); 578 return; 579 } 580 fd_state = 1; 581 fd_retry++; 582 fdintr(0xff); 583 } 584 585 badtrans(dp,bp) 586 struct buf *dp,*bp; 587 { 588 589 bp->b_flags |= B_ERROR; 590 bp->b_error = EIO; 591 bp->b_resid = bp->b_bcount - fd_skip; 592 dp->b_actf = bp->av_forw; 593 fd_skip = 0; 594 biodone(bp); 595 nextstate(dp); 596 597 } 598 599 /* 600 nextstate : After a transfer is done, continue processing 601 requests on the current drive queue. If empty, go to 602 the other drives queue. If that is empty too, timeout 603 to turn off the current drive in 5 seconds, and go 604 to state 0 (not expecting any interrupts). 605 */ 606 607 nextstate(dp) 608 struct buf *dp; 609 { 610 611 if (dp->b_actf) 612 fdstart(fd_drive); 613 else { 614 #if NFD > 1 615 struct buf *dpother; 616 617 dpother = &fd_unit[fd_drive ? 0 : 1].head; 618 619 if (dpother->b_actf) { 620 #ifdef FDTEST 621 printf("switch|"); 622 #endif 623 untimeout(fd_turnoff,fd_drive); 624 timeout(fd_turnoff,fd_drive,5*hz); 625 fd_drive = 1 - fd_drive; 626 dp->b_active = 0; 627 dpother->b_active = 1; 628 fdstart(fd_drive); 629 } else 630 #endif 631 { 632 #ifdef FDTEST 633 printf("off|"); 634 #endif 635 untimeout(fd_turnoff,fd_drive); 636 timeout(fd_turnoff,fd_drive,5*hz); 637 fd_state = 0; 638 dp->b_active = 0; 639 } 640 } 641 } 642 643 Fdioctl() {} 644 Fddump() {} 645 #endif 646