Try to check for IDE drive 0 responding to drive 1 commands.
[seabios.git] / src / ata.c
index e9f9c7cf855c0d01cf461d2517b0f41c37c948a8..3b609c5b73ad1644b1fdcaf86fdb6d71d14cc86f 100644 (file)
--- a/src/ata.c
+++ b/src/ata.c
@@ -28,7 +28,7 @@
 #define IDE_SECTOR_SIZE 512
 #define CDROM_SECTOR_SIZE 2048
 
-#define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
+#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops
 
 struct ata_s ATA VAR16_32;
 
@@ -68,7 +68,7 @@ await_rdy(u16 base)
 }
 
 // Wait for ide state - pauses for one ata cycle first.
-static __always_inline int
+static inline int
 pause_await_not_bsy(u16 iobase1, u16 iobase2)
 {
     // Wait one PIO transfer cycle.
@@ -78,7 +78,7 @@ pause_await_not_bsy(u16 iobase1, u16 iobase2)
 }
 
 // Wait for ide state - pause for 400ns first.
-static __always_inline int
+static inline int
 ndelay_await_not_bsy(u16 iobase1)
 {
     ndelay(400);
@@ -110,7 +110,7 @@ ata_reset(int driveid)
         u64 end = calc_future_tsc(IDE_TIMEOUT);
         for (;;) {
             outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH);
-            status = await_not_bsy(iobase1);
+            status = ndelay_await_not_bsy(iobase1);
             if (status < 0)
                 goto done;
             if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1)
@@ -177,7 +177,7 @@ send_cmd(int driveid, struct ata_pio_command *cmd)
     outb(newdh, iobase1 + ATA_CB_DH);
     if ((olddh ^ newdh) & (1<<4)) {
         // Was a device change - wait for device to become not busy.
-        status = await_not_bsy(iobase1);
+        status = ndelay_await_not_bsy(iobase1);
         if (status < 0)
             return status;
     }
@@ -349,7 +349,7 @@ ata_transfer_cdemu(const struct disk_op_s *op, int before, int after)
  * ATA hard drive functions
  ****************************************************************/
 
-static noinline int
+static int
 send_cmd_disk(const struct disk_op_s *op)
 {
     u64 lba = op->lba;
@@ -379,7 +379,7 @@ send_cmd_disk(const struct disk_op_s *op)
 }
 
 // Read/write count blocks from a harddrive.
-__always_inline int
+int
 ata_cmd_data(struct disk_op_s *op)
 {
     int ret = send_cmd_disk(op);
@@ -394,7 +394,7 @@ ata_cmd_data(struct disk_op_s *op)
  ****************************************************************/
 
 // Low-level atapi command transmit function.
-static __always_inline int
+static int
 send_atapi_cmd(int driveid, u8 *cmdbuf, u8 cmdlen, u16 blocksize)
 {
     u8 channel = driveid / 2;
@@ -422,8 +422,11 @@ send_atapi_cmd(int driveid, u8 *cmdbuf, u8 cmdlen, u16 blocksize)
         return status;
 
     if (status & ATA_CB_STAT_ERR) {
-        dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n"
-                , status, inb(iobase1 + ATA_CB_ERR));
+        u8 err = inb(iobase1 + ATA_CB_ERR);
+        // skip "Not Ready"
+        if (err != 0x20)
+            dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n"
+                    , status, err);
         return -2;
     }
     if (!(status & ATA_CB_STAT_DRQ)) {
@@ -454,7 +457,7 @@ send_cmd_cdrom(const struct disk_op_s *op)
 }
 
 // Read sectors from the cdrom.
-__always_inline int
+int
 cdrom_read(struct disk_op_s *op)
 {
     int ret = send_cmd_cdrom(op);
@@ -466,7 +469,7 @@ cdrom_read(struct disk_op_s *op)
 
 // Pretend the cdrom has 512 byte sectors (instead of 2048) and read
 // sectors.
-__always_inline int
+int
 cdrom_read_512(struct disk_op_s *op)
 {
     u32 vlba = op->lba;
@@ -644,11 +647,10 @@ extract_identify(int driveid, u16 *buffer)
 }
 
 static int
-init_drive_atapi(int driveid)
+init_drive_atapi(int driveid, u16 *buffer)
 {
     // Send an IDENTIFY_DEVICE_PACKET command to device
-    u16 buffer[256];
-    memset(buffer, 0, sizeof(buffer));
+    memset(buffer, 0, IDE_SECTOR_SIZE);
     struct disk_op_s dop;
     dop.driveid = driveid;
     dop.command = ATA_CMD_IDENTIFY_DEVICE_PACKET;
@@ -682,11 +684,10 @@ init_drive_atapi(int driveid)
 }
 
 static int
-init_drive_ata(int driveid)
+init_drive_ata(int driveid, u16 *buffer)
 {
     // Send an IDENTIFY_DEVICE command to device
-    u16 buffer[256];
-    memset(buffer, 0, sizeof(buffer));
+    memset(buffer, 0, IDE_SECTOR_SIZE);
     struct disk_op_s dop;
     dop.driveid = driveid;
     dop.command = ATA_CMD_IDENTIFY_DEVICE;
@@ -735,10 +736,34 @@ init_drive_ata(int driveid)
     return 0;
 }
 
+static int
+powerup_await_non_bsy(u16 base, u64 end)
+{
+    u8 orstatus = 0;
+    u8 status;
+    for (;;) {
+        status = inb(base+ATA_CB_STAT);
+        if (!(status & ATA_CB_STAT_BSY))
+            break;
+        orstatus |= status;
+        if (orstatus == 0xff) {
+            dprintf(1, "powerup IDE floating\n");
+            return orstatus;
+        }
+        if (rdtscll() > end) {
+            dprintf(1, "powerup IDE time out\n");
+            return -1;
+        }
+    }
+    dprintf(6, "powerup iobase=%x st=%x\n", base, status);
+    return status;
+}
+
 static void
 ata_detect()
 {
     // Device detection
+    u64 end = calc_future_tsc(IDE_TIMEOUT);
     int driveid, last_reset_driveid=-1;
     for(driveid=0; driveid<CONFIG_MAX_ATA_DEVICES; driveid++) {
         u8 channel = driveid / 2;
@@ -748,20 +773,27 @@ ata_detect()
         if (!iobase1)
             break;
 
-        // Look for device
-        outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1+ATA_CB_DH);
-        outb(0x55, iobase1+ATA_CB_SC);
-        outb(0xaa, iobase1+ATA_CB_SN);
-        outb(0xaa, iobase1+ATA_CB_SC);
-        outb(0x55, iobase1+ATA_CB_SN);
-        outb(0x55, iobase1+ATA_CB_SC);
-        outb(0xaa, iobase1+ATA_CB_SN);
+        // Wait for not-bsy.
+        int status = powerup_await_non_bsy(iobase1, end);
+        if (status < 0)
+            continue;
+        u8 newdh = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0;
+        outb(newdh, iobase1+ATA_CB_DH);
+        ndelay(400);
+        status = powerup_await_non_bsy(iobase1, end);
+        if (status < 0)
+            continue;
 
         // Check if ioport registers look valid.
+        outb(newdh, iobase1+ATA_CB_DH);
+        u8 dh = inb(iobase1+ATA_CB_DH);
+        outb(0x55, iobase1+ATA_CB_SC);
+        outb(0xaa, iobase1+ATA_CB_SN);
         u8 sc = inb(iobase1+ATA_CB_SC);
         u8 sn = inb(iobase1+ATA_CB_SN);
-        dprintf(6, "ata_detect drive=%d sc=%x sn=%x\n", driveid, sc, sn);
-        if (sc != 0x55 || sn != 0xaa)
+        dprintf(6, "ata_detect drive=%d sc=%x sn=%x dh=%x\n"
+                , driveid, sc, sn, dh);
+        if (sc != 0x55 || sn != 0xaa || dh != newdh)
             continue;
 
         // reset the channel
@@ -773,23 +805,35 @@ ata_detect()
         }
 
         // check for ATAPI
-        int ret = init_drive_atapi(driveid);
-        if (!ret)
+        u16 buffer[256];
+        int ret = init_drive_atapi(driveid, buffer);
+        if (!ret) {
             // Found an ATAPI drive.
-            continue;
-
-        u8 st = inb(iobase1+ATA_CB_STAT);
-        if (!st)
-            // Status not set - can't be a valid drive.
-            continue;
-
-        // Wait for RDY.
-        ret = await_rdy(iobase1);
-        if (ret < 0)
-            continue;
+        } else {
+            u8 st = inb(iobase1+ATA_CB_STAT);
+            if (!st)
+                // Status not set - can't be a valid drive.
+                continue;
+
+            // Wait for RDY.
+            ret = await_rdy(iobase1);
+            if (ret < 0)
+                continue;
+
+            // check for ATA.
+            ret = init_drive_ata(driveid, buffer);
+            if (ret)
+                // No ATA drive found
+                continue;
+        }
 
-        // check for ATA.
-        init_drive_ata(driveid);
+        u16 resetresult = buffer[93];
+        dprintf(6, "ata_detect resetresult=%04x\n", resetresult);
+        if (!slave && (resetresult & 0xdf61) == 0x4041)
+            // resetresult looks valid and device 0 is responding to
+            // device 1 requests - device 1 must not be present - skip
+            // detection.
+            driveid++;
     }
 
     printf("\n");