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