[SeaBIOS] [PATCH] Fixes to the floppy driver, so it works on real (not emulated) hardware

Nikolay Nikolov nickysn at users.sourceforge.net
Tue Jan 30 04:41:47 CET 2018


Several fixes to the floppy driver, so it works on a real floppy controller
with a real floppy. Tested with Coreboot on a motherboard with an ITE IT8712
Super I/O chip (and its built-in floppy controller) and a 1.44MB floppy.

Only one floppy is supported for now and only 1.44MB, but more formats will be
added in the future.

Signed-off-by: Nikolay Nikolov <nickysn at users.sourceforge.net>
---
 src/hw/floppy.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 69 insertions(+), 14 deletions(-)

diff --git a/src/hw/floppy.c b/src/hw/floppy.c
index f2577c5..674909d 100644
--- a/src/hw/floppy.c
+++ b/src/hw/floppy.c
@@ -34,6 +34,11 @@
 #define FLOPPY_GAPLEN 0x1B
 #define FLOPPY_FORMAT_GAPLEN 0x6c
 #define FLOPPY_PIO_TIMEOUT 1000
+#define FLOPPY_IRQ_TIMEOUT 5000
+#define FLOPPY_STARTUP_TIME 8
+#define FLOPPY_MOTOR_HOLD 255 // Magic value, means motor must not be turned off
+#define FLOPPY_SPECIFY1 0xAF  // step rate 12ms, head unload 240ms
+#define FLOPPY_SPECIFY2 0x02  // head load time 4ms, DMA used
 
 // New diskette parameter table adding 3 parameters from IBM
 // Since no provisions are made for multiple drive types, most
@@ -191,7 +196,8 @@ static void
 floppy_disable_controller(void)
 {
     dprintf(2, "Floppy_disable_controller\n");
-    floppy_dor_write(0x00);
+    // Clear the reset bit (enter reset state) and clear 'enable IRQ and DMA'
+    floppy_dor_write(GET_LOW(FloppyDOR) & ~0x0c);
 }
 
 static int
@@ -199,8 +205,9 @@ floppy_wait_irq(void)
 {
     u8 frs = GET_BDA(floppy_recalibration_status);
     SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
+    u32 end = timer_calc(FLOPPY_IRQ_TIMEOUT);
     for (;;) {
-        if (!GET_BDA(floppy_motor_counter)) {
+        if (timer_check(end)) {
             warn_timeout();
             floppy_disable_controller();
             return DISK_RET_ETIMEOUT;
@@ -226,6 +233,7 @@ floppy_wait_irq(void)
 #define FC_READ        (0xe6 | (8<<8) | (7<<12) | FCF_WAITIRQ)
 #define FC_WRITE       (0xc5 | (8<<8) | (7<<12) | FCF_WAITIRQ)
 #define FC_FORMAT      (0x4d | (5<<8) | (7<<12) | FCF_WAITIRQ)
+#define FC_SPECIFY     (0x03 | (2<<8) | (0<<12))
 
 // Send the specified command and it's parameters to the floppy controller.
 static int
@@ -302,9 +310,12 @@ static int
 floppy_enable_controller(void)
 {
     dprintf(2, "Floppy_enable_controller\n");
-    SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
-    floppy_dor_write(0x00);
-    floppy_dor_write(0x0c);
+    // Clear the reset bit (enter reset state), but set 'enable IRQ and DMA'
+    floppy_dor_write((GET_LOW(FloppyDOR) & ~0x04) | 0x08);
+    // Real hardware needs a 4 microsecond delay
+    udelay(4);
+    // Set the reset bit (normal operation) and keep 'enable IRQ and DMA' on
+    floppy_dor_write(GET_LOW(FloppyDOR) | 0x0c);
     int ret = floppy_wait_irq();
     if (ret)
         return ret;
@@ -313,6 +324,30 @@ floppy_enable_controller(void)
     return floppy_pio(FC_CHECKIRQ, param);
 }
 
+static void
+floppy_turn_on_motor(u8 floppyid)
+{
+    // prevent the motor from being turned off
+    SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_HOLD);
+
+    u8 motor_mask = 0x10 << floppyid;
+    if (GET_LOW(FloppyDOR) & motor_mask) {
+        // If the motor has been started, there's no need to wait for it again
+        floppy_dor_write(motor_mask | 0x0c | floppyid);
+    } else {
+        floppy_dor_write(motor_mask | 0x0c | floppyid);
+
+        msleep(FLOPPY_STARTUP_TIME * 125);
+    }
+}
+
+static void
+floppy_turn_off_motor_delayed(void)
+{
+    // reset the disk motor timeout value of INT 08
+    SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
+}
+
 // Activate a drive and send a command to it.
 static int
 floppy_drive_pio(u8 floppyid, int command, u8 *param)
@@ -324,11 +359,8 @@ floppy_drive_pio(u8 floppyid, int command, u8 *param)
             return ret;
     }
 
-    // reset the disk motor timeout value of INT 08
-    SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
-
-    // Turn on motor of selected drive, DMA & int enabled, normal operation
-    floppy_dor_write((floppyid ? 0x20 : 0x10) | 0x0c | floppyid);
+    // Turn on motor of selected drive and wait for it to get up to speed
+    floppy_turn_on_motor(floppyid);
 
     // Send command.
     int ret = floppy_pio(command, param);
@@ -363,6 +395,15 @@ floppy_drive_recal(u8 floppyid)
     return DISK_RET_SUCCESS;
 }
 
+static int
+floppy_drive_specify(void)
+{
+    u8 param[2];
+    param[0] = FLOPPY_SPECIFY1;
+    param[1] = FLOPPY_SPECIFY2;
+    return floppy_pio(FC_SPECIFY, param);
+}
+
 static int
 floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
 {
@@ -433,13 +474,23 @@ floppy_prep(struct drive_s *drive_gf, u8 cylinder)
         !(GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED)) {
         // Recalibrate drive.
         int ret = floppy_drive_recal(floppyid);
-        if (ret)
+        if (ret) {
+            floppy_turn_off_motor_delayed();
             return ret;
+        }
 
         // Sense media.
         ret = floppy_media_sense(drive_gf);
-        if (ret)
+        if (ret) {
+            floppy_turn_off_motor_delayed();
+            return ret;
+        }
+
+        ret = floppy_drive_specify();
+        if (ret) {
+            floppy_turn_off_motor_delayed();
             return ret;
+        }
     }
 
     // Seek to cylinder if needed.
@@ -449,8 +500,10 @@ floppy_prep(struct drive_s *drive_gf, u8 cylinder)
         param[0] = floppyid;
         param[1] = cylinder;
         int ret = floppy_drive_pio(floppyid, FC_SEEK, param);
-        if (ret)
+        if (ret) {
+            floppy_turn_off_motor_delayed();
             return ret;
+        }
         SET_BDA(floppy_track[floppyid], cylinder);
     }
 
@@ -489,9 +542,11 @@ floppy_dma_cmd(struct disk_op_s *op, int count, int command, u8 *param)
         dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n"
                 , param[0], param[1], param[2], param[3]
                 , param[4], param[5], param[6]);
+        floppy_turn_off_motor_delayed();
         return DISK_RET_ECONTROLLER;
     }
 
+    floppy_turn_off_motor_delayed();
     return DISK_RET_SUCCESS;
 }
 
@@ -663,7 +718,7 @@ floppy_tick(void)
 
     // time to turn off drive(s)?
     u8 fcount = GET_BDA(floppy_motor_counter);
-    if (fcount) {
+    if (fcount && (fcount != FLOPPY_MOTOR_HOLD)) {
         fcount--;
         SET_BDA(floppy_motor_counter, fcount);
         if (fcount == 0)
-- 
2.14.3




More information about the SeaBIOS mailing list