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