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