|
|
1.1 root 1: /*
2: * io.386/fdc.c
3: *
4: * Support 765-style controller for diskette and floppy tape
5: *
6: * Revised: Wed Jun 9 12:15:08 1993 CDT
7: */
8:
9: /*
10: * ----------------------------------------------------------------------
11: * Includes.
12: */
13: #include <sys/coherent.h>
14:
15: #include <errno.h>
16: #include <sys/buf.h>
17: #include <sys/con.h>
18: #include <sys/devices.h>
19: #include <sys/dmac.h>
20: #include <sys/fdc765.h>
21: #include <sys/stat.h>
22:
23: /*
24: * ----------------------------------------------------------------------
25: * Definitions.
26: * Constants.
27: * Macros with argument lists.
28: * Typedefs.
29: * Enums.
30: */
31:
32: /* Number of ticks to busy-wait the kernel before timeout awaiting RQM. */
33: #define FDC_RQM_WAIT 2
34:
35: enum {
36: FL_TIMING = 1,
37: FT_TIMING = 2
38: };
39:
40: enum {
41: FL_INTR = 1,
42: FT_INTR = 2
43: };
44:
45: /*
46: * ----------------------------------------------------------------------
47: * Functions.
48: * Import Functions.
49: * Export Functions.
50: * Local Functions.
51: */
52: extern int nulldev();
53: extern unsigned int inb();
54:
55: void fdcCmdStatus();
56: void fdcDrvSelect();
57: void fdcDrvStatus();
58: int fdcGet();
59: void fdcIntStatus();
60: int fdcPut();
61: void fdcRecal();
62: void fdcReset();
63: void fdcResetSel();
64: void fdcSense();
65: void fdcSpecify();
66: void fdcStatus();
67:
68: int setFlIntr();
69: void setFlTimer();
70: int setFtIntr();
71: void setFtTimer();
72:
73: static int fdcload();
74: static int fdcunload();
75: static int fdcopen();
76: static int fdcclose();
77: static int fdcblock();
78: static int fdcread();
79: static int fdcwrite();
80: static int fdcioctl();
81: static int fdctimeout();
82:
83: static void fdcIntr();
84: static void fdcRate();
85: static int fdcWaitRQM();
86:
87: static int setFdcIntr();
88: static void setFdcTiming();
89:
90: /*
91: * ----------------------------------------------------------------------
92: * Global Data.
93: * Import Variables.
94: * Export Variables.
95: * Local Variables.
96: */
97:
98: CON fdccon = {
99: DFBLK | DFCHR, /* Flags */
100: FL_MAJOR, /* Major index */
101: fdcopen, /* Open */
102: fdcclose, /* Close */
103: fdcblock, /* Block */
104: fdcread, /* Read */
105: fdcwrite, /* Write */
106: fdcioctl, /* Ioctl */
107: nulldev, /* Powerfail */
108: fdctimeout, /* Timeout */
109: fdcload, /* Load */
110: fdcunload /* Unload */
111: };
112:
113: /*
114: * Two patchable pointers, for enabling diskette and/or tape device control.
115: */
116: CON * flCon = NULL;
117: CON * ftCon = NULL;
118:
119: /* Global struct "fdc" passes FDC status to diskette and tape drivers. */
120: struct FDC fdc;
121:
122: void (*flIntr)();
123: void (*ftIntr)();
124:
125: static int fdcIntOwner;
126: static int fdcTiming;
127:
128: /*
129: * ----------------------------------------------------------------------
130: * Code.
131: */
132:
133: /***************************************************************************/
134: /*
135: * First part of fdc module.
136: *
137: * Kernel interface.
138: */
139:
140: /*
141: * The load routine asks the
142: * switches how many drives are present
143: * in the machine, and sets up the field
144: * in the floppy database. It also grabs
145: * the level 6 interrupt vector.
146: */
147: static int
148: fdcload()
149: {
150: register int s;
151:
152: if (flCon == NULL && ftCon == NULL) {
153: printf("fdc has no target devices\n");
154: return;
155: }
156:
157: /*
158: * Ensure DMA channel 2 is turned off.
159: * The Computerland ROM does not disable DMA channel after autoboot
160: * from hard disk. The Western Digital controller board appears to
161: * send a dma burst when the floppy controller chip is reset.
162: */
163: dmaoff(DMA_CH2);
164:
165: if (flCon)
166: (*flCon->c_load)();
167: if (ftCon)
168: (*ftCon->c_load)();
169:
170: /*
171: * Initialize the floppy disk controller (if we
172: * have any floppy drives).
173: */
174: s = sphi();
175:
176: setivec(6, fdcIntr);
177: fdcReset();
178:
179: spl(s);
180: }
181:
182: /*
183: * Release resources.
184: */
185: static int
186: fdcunload()
187: {
188: if (flCon == NULL && ftCon == NULL)
189: return;
190:
191: if (flCon)
192: (*flCon->c_uload)();
193: if (ftCon)
194: (*ftCon->c_uload)();
195:
196: /*
197: * Cancel periodic (1 second) invocation.
198: */
199: drvl[FL_MAJOR].d_time = 0;
200: fdcTiming = 0;
201:
202: /*
203: * Turn motors off.
204: */
205: outb(FDCDOR, DORNMR); /* Leave interrupts disabled. */
206:
207: /*
208: * Clear interrupt vector.
209: */
210: clrivec(6);
211: }
212:
213: static int
214: fdcopen(dev, mode)
215: dev_t dev;
216: int mode;
217: {
218: if (FDC_DISKETTE(dev)) {
219: if (flCon)
220: (*flCon->c_open)(dev, mode);
221: else {
222: SET_U_ERROR(ENXIO, "fdcopen()-no floppy");
223: return;
224: }
225: } else if (FDC_TAPE(dev)) {
226: if (ftCon)
227: (*ftCon->c_open)(dev, mode);
228: else {
229: SET_U_ERROR(ENXIO, "fdcopen()-no tape");
230: return;
231: }
232: } else {
233: SET_U_ERROR(ENXIO, "fdcopen()-no device");
234: return;
235: }
236: }
237:
238: static int
239: fdcclose(dev, mode)
240: dev_t dev;
241: int mode;
242: {
243: if (FDC_DISKETTE(dev)) {
244: if (flCon)
245: (*flCon->c_close)(dev, mode);
246: else {
247: SET_U_ERROR(ENXIO, "fdcclose()-no floppy");
248: return;
249: }
250: } else if (FDC_TAPE(dev)) {
251: if (ftCon)
252: (*ftCon->c_close)(dev, mode);
253: else {
254: SET_U_ERROR(ENXIO, "fdcclose()-no tape");
255: return;
256: }
257: } else {
258: SET_U_ERROR(ENXIO, "fdcclose()-no device");
259: return;
260: }
261: }
262:
263: static int
264: fdcread(dev, iop)
265: dev_t dev;
266: IO *iop;
267: {
268: if (FDC_DISKETTE(dev)) {
269: if (flCon)
270: (*flCon->c_read)(dev, iop);
271: else {
272: SET_U_ERROR(ENXIO, "fdcread()-no floppy");
273: return;
274: }
275: } else if (FDC_TAPE(dev)) {
276: if (ftCon)
277: (*ftCon->c_read)(dev, iop);
278: else {
279: SET_U_ERROR(ENXIO, "fdcread()-no tape");
280: return;
281: }
282: } else {
283: SET_U_ERROR(ENXIO, "fdcread()-no device");
284: return;
285: }
286: }
287:
288: static int
289: fdcwrite(dev, iop)
290: dev_t dev;
291: IO *iop;
292: {
293: if (FDC_DISKETTE(dev)) {
294: if (flCon)
295: (*flCon->c_write)(dev, iop);
296: else {
297: SET_U_ERROR(ENXIO, "fdcwrite()-no floppy");
298: return;
299: }
300: } else if (FDC_TAPE(dev)) {
301: if (ftCon)
302: (*ftCon->c_write)(dev, iop);
303: else {
304: SET_U_ERROR(ENXIO, "fdcwrite()-no tape");
305: return;
306: }
307: } else {
308: SET_U_ERROR(ENXIO, "fdcwrite()-no device");
309: return;
310: }
311: }
312:
313: static int
314: fdcioctl(dev, com, par)
315: dev_t dev;
316: int com;
317: char *par;
318: {
319: if (FDC_DISKETTE(dev)) {
320: if (flCon)
321: (*flCon->c_ioctl)(dev, com, par);
322: else {
323: SET_U_ERROR(ENXIO, "fdcioctl()-no floppy");
324: return;
325: }
326: } else if (FDC_TAPE(dev)) {
327: if (ftCon)
328: (*ftCon->c_ioctl)(dev, com, par);
329: else {
330: SET_U_ERROR(ENXIO, "fdcioctl()-no tape");
331: return;
332: }
333: } else {
334: SET_U_ERROR(ENXIO, "fdcioctl()-no device");
335: return;
336: }
337: }
338:
339: /* Can't set u.u_error inside block routine. */
340: static int
341: fdcblock(bp)
342: BUF *bp;
343: {
344: if (FDC_DISKETTE(bp->b_dev)) {
345: if (flCon)
346: (*flCon->c_block)(bp);
347: } else if (FDC_TAPE(bp->b_dev))
348: if (ftCon)
349: (*ftCon->c_block)(bp);
350: }
351:
352: /***************************************************************************/
353: /*
354: * Second part of fdc module.
355: *
356: * Hardware interface.
357: */
358:
359: /*
360: * Get status (if any) from last command
361: */
362: void
363: fdcCmdStatus()
364: {
365: register int b;
366: register int n = 0; /* # of status bytes read */
367: register int i = 0; /* Timeout count */
368: register int s;
369:
370: s = sphi();
371:
372: /*
373: * Read all the status bytes the controller will give us.
374: */
375:
376: for (;;) {
377: b = fdcGet();
378: if (b == -1)
379: break;
380:
381: if (n < FDC_NUM_CMD_STAT)
382: fdc.fdc_cmdstat[n++] = b;
383: }
384:
385: fdc.fdc_ncmdstat = n;
386: spl(s);
387: }
388:
389: /*
390: * fdcDrvSelect()
391: *
392: * Select the drive indicated by "drive" (0..3).
393: * If "motorOn" is nonzero, turn the motor on as well.
394: */
395: void
396: fdcDrvSelect(drive, motorOn)
397: int drive, motorOn;
398: {
399: unsigned char motorBits = 0;
400:
401: if (drive >= 4) {
402: printf("Can't fdcDrvSelect(%d,%d) ", drive, motorOn);
403: return;
404: }
405:
406: /* If needed, generate motor on bit for current selected drive. */
407: if (motorOn)
408: motorBits = 0x10 << drive;
409:
410: outb(FDCDOR, DORNMR | DORIEN | drive | motorBits);
411: fdcSense(); /* Just in case --- */
412: }
413:
414: /*
415: * Send Sense Drive Status command to FDC.
416: */
417: void
418: fdcDrvStatus(drive, head)
419: int drive, head;
420: {
421: fdcPut(CMDSDRV);
422: fdcPut(drive | (head << 2));
423: }
424:
425: /*
426: * Read NEC data register, doing needed handshake.
427: * Return -1 in case of timeout before RQM or no data available.
428: */
429: int
430: fdcGet()
431: {
432: int ret = -1;
433:
434: /* Wait for RQM, then expect DIO true. */
435: if (busyWait(fdcWaitRQM, FDC_RQM_WAIT) && (inb(FDCMSR) & MSRDIO))
436: ret = inb(FDCDAT);
437:
438: return ret;
439: }
440:
441: /*
442: * Get Interrupt status
443: */
444: void
445: fdcIntStatus()
446: {
447: register int b;
448: register int n = 0; /* # of status bytes read */
449: register int i = 0; /* Timeout count */
450: register int s;
451:
452: s = sphi();
453:
454: /*
455: * Issue a sense interrupt command and stash result.
456: */
457: fdcPut(CMDSINT);
458:
459: n = 0;
460: for (;;) {
461: b = fdcGet();
462: if (b == -1)
463: break;
464:
465: if (n < FDC_NUM_INT_STAT)
466: fdc.fdc_intstat[n++] = b;
467: }
468: fdc.fdc_nintstat = n;
469: spl(s);
470: }
471:
472: /*
473: * Since the FDC is run in DMA mode, possible interrupt causes are:
474: *
475: * 1 Result phase of: Read Data, Read Track, Read ID, Read Deleted Data,
476: * Write Data, Format Cylinder, Write Deleted Data, Scan.
477: *
478: * 2 Ready line of diskette drive changes state.
479: *
480: * 3 End of Seek or Recalibrate.
481: *
482: * In case 1, the interrupt is cleared by read/write data to FDC.
483: * In cases 2 and 3, a Sense Interrupt is needed to clear the interrupt
484: * and determine its cause.
485: *
486: * The following comment is obsolete, but possibly of interest:
487: ***********************************************
488: * The interrupt routine gets all
489: * the status bytes the controller chip
490: * will give it, then issues a sense interrupt
491: * status command (which is necessary for a seek
492: * to complete!) and throws all of the status
493: * bytes away.
494: ***********************************************
495: */
496: static void
497: fdcIntr()
498: {
499: register int s;
500:
501: s = sphi();
502:
503: /* Invalidate previously stored interrupt and command status. */
504: fdc.fdc_nintstat = 0;
505: fdc.fdc_ncmdstat = 0;
506:
507: /* Vector to diskette or tape device interrupt handler. */
508: if ((fdcIntOwner & FL_INTR) && flIntr)
509: (*flIntr)();
510:
511: if ((fdcIntOwner & FT_INTR) && ftIntr)
512: (*ftIntr)();
513: spl(s);
514: }
515:
516: /*
517: * Send a command byte to the NEC chip, first waiting until the chip
518: * says that it is ready.
519: * Return 0 if able to send, or -1 if timed out or FDC had wrong I/O
520: * direction.
521: */
522: int
523: fdcPut(cmd)
524: int cmd;
525: {
526: int ret = -1;
527:
528: /* Wait for RQM, then expect DIO false. */
529: if (busyWait(fdcWaitRQM, FDC_RQM_WAIT)) {
530: if ((inb(FDCMSR) & MSRDIO) == 0) {
531: outb(FDCDAT, cmd);
532: ret = 0;
533: }
534: }
535:
536: return ret;
537: }
538:
539: /*
540: * Set transfer rate (FDC_RATE_250K/300K/500K/1MEG)
541: */
542: void
543: fdcRate(rate)
544: int rate;
545: {
546: outb(FDCRATE, rate);
547: }
548:
549: /*
550: * Given drive # (0..3), send a Recalibrate command to the FDC.
551: *
552: * Sense interrupt - fdcIntStatus() - *must* be done when the
553: * ensuing IRQ happens.
554: */
555: void
556: fdcRecal(drive)
557: int drive;
558: {
559: fdcPut(CMDRCAL);
560: fdcPut(drive);
561: }
562:
563: /*
564: * Given drive # (0..3), head (0..1), and cylinder (0..255),
565: * send a seek command to the FDC.
566: *
567: * Sense interrupt - fdcIntStatus() - *must* be done when the
568: * ensuing IRQ happens.
569: */
570: void
571: fdcSeek(drive, head, cyl)
572: int drive, head, cyl;
573: {
574: fdcPut(CMDSEEK);
575: fdcPut(drive | (head << 2));
576: fdcPut(cyl);
577: }
578:
579: /*
580: * fdcSense() issues Sense Drive Status and Sense Interrupt Status,
581: * saving information from the FDC into global struct "fdc".
582: *
583: * It is called in response to FDC interrupts.
584: */
585: void
586: fdcSense()
587: {
588: fdcCmdStatus(); /* Get command status. */
589: fdcIntStatus(); /* Get int status, just in case. */
590: }
591:
592: /*
593: * Send Specify command and data bytes to FDC.
594: * Always specify DMA mode (ND bit = 0).
595: */
596: void
597: fdcSpecify(srt, hut, hlt)
598: int srt, hut, hlt;
599: {
600: fdcPut(CMDSPEC);
601: fdcPut((srt << 4) | hut);
602: fdcPut(hlt << 1);
603: }
604:
605: /*
606: * Dissassemble the floppy error status for user reference.
607: */
608: void
609: fdcStatus()
610: {
611: printf("fd%d: head=%u",
612: fdc.fdc_cmdstat[0] & 3, (fdc.fdc_cmdstat[0] & 4) >> 1);
613:
614: /*
615: * Report on ST0 bits.
616: */
617: if (fdc.fdc_ncmdstat >= 1) {
618: if (fdc.fdc_cmdstat[0] & ST0_NR)
619: printf(" <Not Ready>");
620:
621: if (fdc.fdc_cmdstat[0] & ST0_EC)
622: printf(" <Equipment Check>");
623: }
624:
625: /*
626: * Report on ST1 bits.
627: */
628: if (fdc.fdc_ncmdstat >= 2) {
629: if (fdc.fdc_cmdstat[1] & ST1_MA)
630: printf(" <Missing Address Mark>");
631:
632: if (fdc.fdc_cmdstat[1] & ST1_NW)
633: printf(" <Write Protected>");
634:
635: if (fdc.fdc_cmdstat[1] & ST1_ND)
636: printf(" <No Data>");
637:
638: if (fdc.fdc_cmdstat[1] & ST1_OR)
639: printf(" <Overrun>");
640:
641: if (fdc.fdc_cmdstat[1] & ST1_DE)
642: printf(" <Data Error>");
643:
644: if (fdc.fdc_cmdstat[1] & ST1_EN)
645: printf(" <End of Cyl>");
646: }
647:
648: /*
649: * Report on ST2 bits.
650: */
651: if (fdc.fdc_ncmdstat >= 3) {
652: if (fdc.fdc_cmdstat[2] & ST2_MD)
653: printf(" <Missing Data Address Mark>");
654:
655: if (fdc.fdc_cmdstat[2] & ST2_BC)
656: printf(" <Bad Cylinder>");
657:
658: if (fdc.fdc_cmdstat[2] & ST2_WC)
659: printf(" <Wrong Cylinder>");
660:
661: if (fdc.fdc_cmdstat[2] & ST2_DD)
662: printf(" <Bad Data CRC>");
663:
664: if (fdc.fdc_cmdstat[2] & ST2_CM)
665: printf(" <Data Deleted>");
666: }
667:
668: printf("\n");
669: }
670:
671: /*
672: * Wait for FDC Main Status Register to assert Request for Master.
673: * This function is designed to be called by busyWait().
674: * It returns nonzero if RQM is asserted, 0 if not.
675: */
676: static int
677: fdcWaitRQM()
678: {
679: return (inb(FDCMSR) & MSRRQM);
680: }
681:
682: /*
683: * If sw is nonzero, start the timer for diskette;
684: * else, stop the timer.
685: */
686: void
687: setFlTimer(sw)
688: int sw;
689: {
690: setFdcTiming(sw, FL_TIMING);
691: }
692:
693: /*
694: * If sw is nonzero, start the timer for floppy tape;
695: * else, stop the timer.
696: */
697: void
698: setFtTimer(sw)
699: int sw;
700: {
701: setFdcTiming(sw, FT_TIMING);
702: }
703:
704: static void
705: setFdcTiming(sw, mask)
706: int sw, mask;
707: {
708: int s = sphi();
709:
710: /*
711: * Only do something if current request changes status of
712: * timing for the device.
713: */
714: if (sw && (fdcTiming & mask) == 0) {
715: fdcTiming |= mask;
716: drvl[FL_MAJOR].d_time = 1;
717: goto setFdcTimingDone;
718: }
719: if (sw == 0 && (fdcTiming & mask)) {
720: fdcTiming &= ~mask;
721: if (fdcTiming == 0)
722: drvl[FL_MAJOR].d_time = 0;
723: goto setFdcTimingDone;
724: }
725:
726: setFdcTimingDone:
727: spl(s);
728: return;
729: }
730:
731: static int
732: fdctimeout()
733: {
734: if ((fdcTiming & FL_TIMING) && flCon)
735: (*flCon->c_timer)();
736: if ((fdcTiming & FT_TIMING) && ftCon)
737: (*ftCon->c_timer)();
738: }
739:
740: /*
741: * Reset the fdc.
742: * Sets drive select bits to 00, motor on bits to 0000.
743: */
744: void
745: fdcReset()
746: {
747: outb(FDCDOR, 0);
748:
749: /* "Not Reset FDC" must remain low for at least 3.5 usec */
750: busyWait2(NULL, 4);
751: outb(FDCDOR, DORNMR | DORIEN);
752: }
753:
754: /*
755: * Reset the fdc.
756: * Maintain drive select and motor enable (as specified) during the reset.
757: */
758: void
759: fdcResetSel(drive, motorOn)
760: int drive, motorOn;
761: {
762: unsigned char motorBits = 0;
763: unsigned char outByte;
764:
765: /* If needed, generate motor on bit for current selected drive. */
766: if (motorOn)
767: motorBits = 0x10 << drive;
768:
769: /*
770: * Send drive select, motor on if needed, interrupt enable.
771: * The "not reset" bit is zero, which is the point of this routine.
772: */
773: outByte = motorBits | drive | DORIEN;
774: outb(FDCDOR, outByte);
775:
776: /* "Not Reset FDC" must remain low for at least 3.5 usec */
777: busyWait2(NULL, 4);
778:
779: outByte |= DORNMR;
780: outb(FDCDOR, outByte);
781: }
782:
783: /*
784: * If sw is nonzero, try to seize the fdc interrupt for the diskette;
785: * else, try to release it.
786: *
787: * Return 1 on success, 0 on failure.
788: */
789: int
790: setFlIntr(sw)
791: int sw;
792: {
793: return setFdcIntr(sw, FL_INTR);
794: }
795:
796: /*
797: * If sw is nonzero, try to seize the fdc interrupt for floppy tape;
798: * else, try to release it.
799: *
800: * Return 1 on success, 0 on failure.
801: */
802: int
803: setFtIntr(sw)
804: int sw;
805: {
806: return setFdcIntr(sw, FT_INTR);
807: }
808:
809: static int
810: setFdcIntr(sw, mask)
811: int sw, mask;
812: {
813: int ret;
814:
815: /*
816: * if attaching
817: * if fdc interrupt is free
818: * attach as requested
819: * else
820: * return failure
821: * else
822: * if fdc requesting device now owns fdc interrupt
823: * detach as requested
824: * else
825: * return failure
826: */
827: if (sw)
828: if (fdcIntOwner == 0) {
829: fdcIntOwner = mask;
830: ret = 1;
831: } else
832: ret = 0;
833: else
834: if (fdcIntOwner == mask) {
835: fdcIntOwner = 0;
836: ret = 1;
837: } else
838: ret = 0;
839: }
840:
841: /* * * * * End of fdc.c * * * * */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.