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