Modules - Adding rewrite of the FDD driver
[tpg/acess2.git] / Modules / Storage / FDDv2 / fdc.c
1 /*
2  * Acess2 82077AA FDC
3  * - By John Hodge (thePowersGang)
4  *
5  * fdc.c
6  * - FDC IO Functions
7  */
8 #include <acess.h>
9 #include "common.h"
10
11 // === CONSTANTS ===
12 #define MOTOR_ON_DELAY  500
13 #define MOTOR_OFF_DELAY 2000
14
15 enum eMotorState
16 {
17         MOTOR_OFF,
18         MOTOR_ATSPEED,
19 };
20
21 enum eFDC_Registers
22 {
23         FDC_DOR  = 0x02,        // Digital Output Register
24         FDC_MSR  = 0x04,        // Master Status Register (Read Only)
25         FDC_FIFO = 0x05,        // FIFO port
26         FDC_CCR  = 0x07         // Configuration Control Register (write only)
27 };
28
29 enum eFDC_Commands
30 {
31         CMD_SPECIFY = 3,            // Specify parameters
32         CMD_WRITE_DATA = 5,         // Write Data
33         CMD_READ_DATA = 6,          // Read Data
34         CMD_RECALIBRATE = 7,        // Recalibrate a drive
35         CMD_SENSE_INTERRUPT = 8,    // Sense (Ack) an interrupt
36         CMD_SEEK = 15,              // Seek to a track
37 };
38
39 // === PROTOTYPES ===
40  int    FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer);
41
42  int    FDD_int_WriteData(Uint16 Base, Uint8 Data);
43  int    FDD_int_ReadData(Uint16 Base, Uint8 *Data);
44 void    FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl);
45  int    FDD_int_Calibrate(int Disk);
46  int    FDD_int_Reset(Uint16 Base);
47  int    FDD_int_StartMotor(int Disk);
48  int    FDD_int_StopMotor(int Disk);
49 void    FDD_int_StopMotorCallback(void *Ptr);
50 Uint16  FDD_int_GetBase(int Disk, int *Drive);
51 void    FDD_int_ClearIRQ(void);
52  int    FDD_int_WaitIRQ(void);
53 void    FDD_int_IRQHandler(int IRQ, void *Ptr);
54
55 // === GLOBALS ===
56  int    gbFDD_IRQ6Fired;
57
58 // === CODE ===
59 /**
60  * \brief Read/Write data from/to a disk
61  * \param Disk Global disk number
62  * \param Track Track number
63  * \param bWrite Toggle write mode
64  * \param Buffer Destination/Source buffer
65  * \return Boolean failure
66  */
67 int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer)
68 {
69         return -1;
70 }
71
72 /**
73  * \brief Write a byte to the FIFO
74  */
75 int FDD_int_WriteData(Uint16 Base, Uint8 Data)
76 {
77         for( int i = 0; i < 100; i ++ )
78         {
79                 if( inb(Base + FDC_MSR) & 0x80 )
80                 {
81                         outb(Base + FDC_FIFO, Data);
82                         return 0;
83                 }
84                 Time_Delay(10);
85         }
86         Log_Error("FDD", "Write timeout");
87         return 1;
88 }
89
90 /**
91  * \brief Read a byte from the FIFO
92  */
93 int FDD_int_ReadData(Uint16 Base, Uint8 *Data)
94 {
95         for( int i = 0; i < 100; i ++ )
96         {
97                 if( inb(Base + FDC_MSR) & 0x80 )
98                 {
99                         Uint8 tmp = inb(Base + FDC_FIFO);
100                         if(Data) *Data = tmp;
101                         return 0;
102                 }
103                 Time_Delay(10);
104         }
105         Log_Error("FDD", "Read timeout");
106         return 1;
107 }
108
109 /**
110  * \brief Acknowledge an interrupt
111  * \param Base  Controller base address
112  * \param ST0   Location to store the ST0 value
113  * \param Cyl   Current cylinder
114  */
115 void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl)
116 {
117         FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT);
118         FDD_int_ReadData(Base, ST0);
119         FDD_int_ReadData(Base, Cyl);
120 }
121
122 /**
123  * \brief Calibrate a drive
124  * \param Disk  Global disk number
125  */
126 int FDD_int_Calibrate(int Disk)
127 {
128          int    _disk;
129         Uint16  base = FDD_int_GetBase(Disk, &_disk);
130         FDD_int_StartMotor(Disk);
131         
132         for( int i = 0; i < 10; i ++ )
133         {
134                 Uint8   st0=0, cyl = -1;
135         
136                 FDD_int_ClearIRQ();     
137                 FDD_int_WriteData(base, CMD_RECALIBRATE);
138                 FDD_int_WriteData(base, _disk);
139                 
140                 FDD_int_WaitIRQ();
141         
142                 FDD_int_SenseInterrupt(base, &st0, NULL);
143                 
144                 if( st0 & 0xC0 ) {
145                         static const char *status_type[] = {
146                                 0, "Error", "Invalid", "Drive Error"
147                         };
148                         Log_Debug("FDD", "FDD_int_Calibrate: st0 & 0xC0 = 0x%x, %s",
149                                 st0 & 0xC0, status_type[st0 >> 6]
150                                 );
151                         continue ;
152                 }
153                 
154                 if( cyl == 0 )
155                 {
156                         FDD_int_StopMotor(Disk);
157                         return 0;
158                 }
159         }
160         
161         Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted");
162         
163         return 1;
164 }
165
166 /**
167  * \brief Reset a controller
168  * \param Base  Controller base address
169  */
170 int FDD_int_Reset(Uint16 Base)
171 {
172         Uint8   tmp;
173         
174         tmp = inb(Base + FDC_DOR) & 0xF0;
175         outb( Base + FDC_DOR, 0x00 );
176         Time_Delay(1);
177         outb( Base + FDC_DOR, tmp | 0x0C );
178
179         FDD_int_SenseInterrupt(Base, NULL, NULL);
180
181         outb(Base + FDC_CCR, 0x00);     // 500KB/s
182
183         FDD_int_WriteData(Base, CMD_SPECIFY);   // Step and Head Load Times
184         FDD_int_WriteData(Base, 0xDF);  // Step Rate Time, Head Unload Time (Nibble each)
185         FDD_int_WriteData(Base, 0x02);  // Head Load Time >> 1
186
187         // TODO: Recalibrate all present disks
188         FDD_int_Calibrate(0);
189         return 0;
190 }
191
192 /**
193  * \brief Start the motor on a disk
194  */
195 int FDD_int_StartMotor(int Disk)
196 {
197          int    _disk;
198         Uint16  base = FDD_int_GetBase(Disk, &_disk);
199         
200         if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED )
201                 return 0;
202
203         // Clear the motor off timer    
204         Time_RemoveTimer(gaFDD_Disks[Disk].Timer);
205         gaFDD_Disks[Disk].Timer = -1;
206
207         // Turn motor on
208         outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4)));
209
210         // Wait for it to reach speed
211         Time_Delay(MOTOR_ON_DELAY);
212
213         gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED;
214
215         return 0;
216 }
217
218 /**
219  * \brief Schedule the motor to stop
220  */
221 int FDD_int_StopMotor(int Disk)
222 {
223         if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED )
224                 return 0;
225         if( gaFDD_Disks[Disk].Timer != -1 )
226                 return 0;
227
228         gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk);
229
230         return 0;
231 }
232
233 /**
234  * \brief Actually stop the motor
235  * \param Ptr   Actaully the global disk number
236  */
237 void FDD_int_StopMotorCallback(void *Ptr)
238 {
239          int    Disk = (tVAddr)Ptr;
240          int    _disk;
241         Uint16  base = FDD_int_GetBase(Disk, &_disk);
242
243         gaFDD_Disks[Disk].Timer = -1;
244         gaFDD_Disks[Disk].MotorState = MOTOR_OFF;
245         
246         outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4)));
247
248         return ;
249 }
250
251 /**
252  * \brief Converts a global disk number into a controller and drive
253  * \param Disk  Global disk number
254  * \param Drive Destination for controller disk number
255  * \return Controller base address
256  */
257 Uint16 FDD_int_GetBase(int Disk, int *Drive)
258 {
259         if(Drive)       *Drive = Disk & 3;
260         switch(Disk >> 2)
261         {
262         case 0: return 0x3F0;
263         case 1: return 0x370;
264         default:
265                 return 0;
266         }
267 }
268
269 /**
270  * \brief Clear the IRQ fired flag
271  */
272 void FDD_int_ClearIRQ(void)
273 {
274         gbFDD_IRQ6Fired = 0;
275 }
276
277 /**
278  * \brief Wait for an IRQ to fire
279  */
280 int FDD_int_WaitIRQ(void)
281 {
282         while(gbFDD_IRQ6Fired == 0)
283                 Threads_Yield();
284         return 0;
285 }
286
287 /**
288  * \brief IRQ Handler
289  * \param IRQ   IRQ Number (unused)
290  * \param Ptr   Data Pointer (unused)
291  */
292 void FDD_int_IRQHandler(int IRQ, void *Ptr)
293 {
294         gbFDD_IRQ6Fired = 1;
295 }
296

UCC git Repository :: git.ucc.asn.au