Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 16 Oct 2003 23:17:47 +0900
From:      Hiroyuki Aizu <eyes@navi.org>
To:        current@freebsd.org
Subject:   PATCH for ATAng
Message-ID:  <20031016231747.4f0640e3.eyes@navi.org>

next in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Hi.

The original ata_reset() lost ATA-master drive and remove main file system
after suspend/resume. Of cource it occors panic!
I think that the ata_reset() in ata-lowlevel.c is bogus and I can not
understand the code. So I study ATA and rewrite ata_reset() completely.

New device detect algorism using ata command ATA_IDENTIFY_DEVICE and
ATA_IDENTIFY_PACKET_DEVICE for judge ATA and ATAPI devices.

This patch works fine with my TOSHIBA Libretto L5. 
But not yet test ATAPI devices and ATA-slave channel.
Maybe there is need to adjust wait DELAY time.

Please test and replace ata_reset().

I hope this solve ATAng troubles.

--
Hiroyuki Aizu

[-- Attachment #2 --]
--- ata-lowlevel.c.orig	Thu Oct  9 23:33:06 2003
+++ ata-lowlevel.c	Thu Oct 16 22:28:06 2003
@@ -488,162 +488,222 @@
 }
 
 /* must be called with ATA channel locked */
-static void
-ata_reset(struct ata_channel *ch)
+#define ATA_TIMEOUT			10000
+#define ATA_IDENTIFY_DEVICE		0xec
+#define ATA_IDENTIFY_PACKET_DEVICE	0xa1
+
+#define ATA_MASTER_MASK		(ATA_ATA_MASTER | ATA_ATAPI_MASTER)
+#define ATA_SLAVE_MASK		(ATA_ATA_SLAVE | ATA_ATAPI_SLAVE)
+
+static int
+ata_wait_ready(struct ata_channel *ch)
 {
-    u_int8_t err, lsb, msb, ostat0, ostat1;
-    u_int8_t stat0 = 0, stat1 = 0;
-    int mask = 0, timeout;
+    int i, stat;
 
-    /* do we have any signs of ATA/ATAPI HW being present ? */
-    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
-    DELAY(10);
-    ostat0 = ATA_IDX_INB(ch, ATA_STATUS);
-    if ((ostat0 & 0xf8) != 0xf8 && ostat0 != 0xa5) {
-	stat0 = ATA_S_BUSY;
-	mask |= 0x01;
-    }
-
-    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
-    DELAY(10);	
-    ostat1 = ATA_IDX_INB(ch, ATA_STATUS);
-
-    /* in some setups we dont want to test for a slave */
-    if (!(ch->flags & ATA_NO_SLAVE)) {
-	if ((ostat1 & 0xf8) != 0xf8 && ostat1 != 0xa5) {
-	    stat1 = ATA_S_BUSY;
-	    mask |= 0x02;
-	}
+    for (i = 0; i< ATA_TIMEOUT; i++) {
+	DELAY(10);
+        stat = ATA_IDX_INB(ch, ATA_STATUS);
+        if (!(stat & ATA_S_BUSY) && !(stat & ATA_S_DRQ))
+            break;
     }
+    DELAY(10);
 
-    /* if nothing showed up no need to get any further */
-    /* SOS is that too strong?, we just might loose devices here XXX */
-    ch->devices = 0;
-    if (!mask)
-	return;
+    if (i == ATA_TIMEOUT)
+        return -1;
 
-    if (bootverbose)
-	ata_printf(ch, -1, "reset tp1 mask=%02x ostat0=%02x ostat1=%02x\n",
-		   mask, ostat0, ostat1);
+    return 0;
+}
+
+static int
+ata_soft_reset(struct ata_channel *ch)
+{
+    int i, stat;
 
     /* reset channel */
-    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
-    DELAY(10);
     ATA_IDX_OUTB(ch, ATA_ALTSTAT, ATA_A_IDS | ATA_A_RESET);
     DELAY(10000); 
     ATA_IDX_OUTB(ch, ATA_ALTSTAT, ATA_A_IDS);
-    DELAY(10000);
+    DELAY(20000);
+
+    for (i = 0; i< ATA_TIMEOUT; i++) {
+	DELAY(100);
+        /* stat = ATA_IDX_INB(ch, ATA_STATUS); */
+        stat = ATA_IDX_INB(ch, ATA_ALTSTAT);
+        if (!(stat & ATA_S_BUSY))
+            break;
+    }
+    if (i == ATA_TIMEOUT)
+        return -1;
+
+    DELAY(10);
+
+    return 0;
+}
+
+static int
+ata_device_select(struct ata_channel *ch, u_int8_t unit)
+{
+    if (ata_wait_ready(ch))
+        return -1;
+
+    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | unit);
+    DELAY(10);
+
+    if (ata_wait_ready(ch))
+        return -1;
+
+    return 0;
+}
+
+static int
+ata_check_device_existence(struct ata_channel *ch)
+{
+    u_int8_t lsb, msb;
+
+    ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0x55);
+    ATA_IDX_OUTB(ch, ATA_CYL_MSB, 0xaa);
+
+    lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
+    msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
+
+    if (lsb != 0x55 || msb != 0xaa) {
+        /* not found */
+        ata_printf(ch, -1, "check dev existence:lsb=%02x msb=%02x\n", lsb, msb);
+        return 0;
+    }
+
+    /* found */
+    return 1;
+}
+
+static int
+identify_device(struct ata_channel *ch, int device, u_int8_t type)
+{
+    int i, j;
+    u_int8_t stat;
+
+    ATA_IDX_OUTB(ch, ATA_ALTSTAT, ATA_A_IDS);
+
+    ATA_IDX_OUTB(ch, ATA_FEATURE, 0);
+    ATA_IDX_OUTB(ch, ATA_COUNT, 0);
+    ATA_IDX_OUTB(ch, ATA_SECTOR, 0);
+    ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0);
+    ATA_IDX_OUTB(ch, ATA_CYL_MSB, 0);
+    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | device);
+    ATA_IDX_OUTB(ch, ATA_CMD, type);
+    DELAY(10);
 
-    /* wait for BUSY to go inactive */
-    for (timeout = 0; timeout < 310; timeout++) {
-	if (stat0 & ATA_S_BUSY) {
-	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
-	    DELAY(10);
-    	    err = ATA_IDX_INB(ch, ATA_ERROR);
-	    lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
-	    msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
-	    stat0 = ATA_IDX_INB(ch, ATA_STATUS);
-	    if (bootverbose)
-		ata_printf(ch, ATA_MASTER,
-			   "stat=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
-			   stat0, err, lsb, msb);
-	    if (!(stat0 & ATA_S_BUSY)) {
-		if ((err & 0x7f) == ATA_E_ILI) {
-		    if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
-			ch->devices |= ATA_ATAPI_MASTER;
-		    }
-		    else if (stat0 & ATA_S_READY) {
-			ch->devices |= ATA_ATA_MASTER;
-		    }
-		}
-		else if ((stat0 & 0x4f) && err == lsb && err == msb) {
-		    stat0 |= ATA_S_BUSY;
-		}
-	    }
-	}
-	if (stat1 & ATA_S_BUSY) {
-	    ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
-	    DELAY(10);
-    	    err = ATA_IDX_INB(ch, ATA_ERROR);
-	    lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
-	    msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
-	    stat1 = ATA_IDX_INB(ch, ATA_STATUS);
-	    if (bootverbose)
-		ata_printf(ch, ATA_SLAVE,
-			   " stat=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
-			   stat1, err, lsb, msb);
-	    if (!(stat1 & ATA_S_BUSY)) {
-		if ((err & 0x7f) == ATA_E_ILI) {
-		    if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
-			ch->devices |= ATA_ATAPI_SLAVE;
-		    }
-		    else if (stat1 & ATA_S_READY) {
-			ch->devices |= ATA_ATA_SLAVE;
-		    }
-		}
-		else if ((stat1 & 0x4f) && err == lsb && err == msb) {
-		    stat1 |= ATA_S_BUSY;
-		}
-	    }
-	}
-	if (mask == 0x01)	/* wait for master only */
-	    if (!(stat0 & ATA_S_BUSY) || (stat0 == 0xff && timeout > 20))
-		break;
-	if (mask == 0x02)	/* wait for slave only */
-	    if (!(stat1 & ATA_S_BUSY) || (stat1 == 0xff && timeout > 20))
-		break;
-	if (mask == 0x03)	/* wait for both master & slave */
-	    if ((!(stat0 & ATA_S_BUSY) || (stat0 == 0xff && timeout > 20)) &&
-	        (!(stat1 & ATA_S_BUSY) || (stat1 == 0xff && timeout > 20)))
-		break;
-	DELAY(100000);
-    }	
-
-    if (stat0 & ATA_S_BUSY)
-	mask &= ~0x01;
-    if (stat1 & ATA_S_BUSY)
-	mask &= ~0x02;
+    ATA_IDX_INB(ch, ATA_ALTSTAT);
+
+    for (j = 0; j < ATA_TIMEOUT; j++) {
+        stat = ATA_IDX_INB(ch, ATA_ALTSTAT);
+        if (!(stat & ATA_S_BUSY))
+            break;
+    }
+    if (j == ATA_TIMEOUT) {
+        if (bootverbose)
+            ata_printf(ch, -1, "identify timeout %02x\n", stat);
+        return -1;
+    }
+
+    if (stat & ATA_S_ERROR) {
+        ATA_IDX_INB(ch, ATA_STATUS);
+        if (bootverbose)
+            ata_printf(ch, -1, "identify error %02x\n", stat);
+        return -1;
+    }
+
+    if (!(stat & ATA_S_DRQ)) {
+        ATA_IDX_INB(ch, ATA_STATUS);
+        if (bootverbose)
+            ata_printf(ch, -1, "identify drq %02x\n", stat);
+        return -1;
+    }
+
+    for (i = 0; i < 256; i++) {
+        ATA_IDX_INW(ch, ATA_DATA);
+    }
+
+    ATA_IDX_INB(ch, ATA_STATUS);
+    return 0;
+}
+
+static void
+ata_reset(struct ata_channel *ch)
+{
+    int mask = 0;
+    int devices = 0;
+    int ret;
+
+    ch->devices = 0;
+
+    /* reset */
+    if (ata_soft_reset(ch)) {
+        if (bootverbose)
+            ata_printf(ch, -1, "error or timeout while reset\n");
+        return;
+    }
+
+    /* check MASTER */
+    if (ata_device_select(ch, ATA_MASTER))
+        return;
+    ret = ata_check_device_existence(ch);
+    if (ret > 0) {
+        mask |= 0x01;
+    }
+
+    /* check SLAVE */
+    if (ata_device_select(ch, ATA_SLAVE))
+        return;
+    ret = ata_check_device_existence(ch);
+    if (ret > 0) {
+        mask |= 0x02;
+    }
+
+    /* ATA MASTER */
+    if (mask & 0x01) {
+        if (!ata_device_select(ch, ATA_MASTER))
+            if (!identify_device(ch, ATA_MASTER, ATA_IDENTIFY_DEVICE))
+                devices |= ATA_ATA_MASTER;
+    }
+
+    /* ATA SLAVE */
+    if (mask & 0x02) {
+        if (!ata_device_select(ch, ATA_SLAVE))
+            if (!identify_device(ch, ATA_SLAVE, ATA_IDENTIFY_DEVICE))
+                devices |= ATA_ATA_SLAVE;
+    }
 
     if (bootverbose)
-	ata_printf(ch, -1,
-		   "reset tp2 mask=%02x stat0=%02x stat1=%02x devices=0x%b\n",
-		   mask, stat0, stat1, ch->devices,
-		   "\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
-#if 0
-    if (!mask)
-	return;
-
-    if (mask & 0x01 && ostat0 != 0x00 &&
-        !(ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER))) {
-	ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
-	DELAY(10);
-	ATA_IDX_OUTB(ch, ATA_ERROR, 0x58);
-	ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0xa5);
-	err = ATA_IDX_INB(ch, ATA_ERROR);
-	lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
-	if (bootverbose)
-	    ata_printf(ch, ATA_MASTER, "ATA err=0x%02x lsb=0x%02x\n", err, lsb);
-	if (err != 0x58 && lsb == 0xa5)
-	    ch->devices |= ATA_ATA_MASTER;
-    }
-    if (mask & 0x02 && ostat1 != 0x00 &&
-	!(ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE))) {
-	ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
-	DELAY(10);
-	ATA_IDX_OUTB(ch, ATA_ERROR, 0x58);
-	ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0xa5);
-	err = ATA_IDX_INB(ch, ATA_ERROR);
-	lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
-	if (bootverbose)
-	    ata_printf(ch, ATA_SLAVE, "ATA err=0x%02x lsb=0x%02x\n", err, lsb);
-	if (err != 0x58 && lsb == 0xa5)
-	    ch->devices |= ATA_ATA_SLAVE;
+        ata_printf(ch, -1, "reset check ATA: devices=%02x\n", devices);
+
+    /* ATAPI MASTER */
+    if (mask & 0x01) {
+        if (!ata_device_select(ch, ATA_MASTER))
+            if (!identify_device(ch, ATA_MASTER, ATA_IDENTIFY_PACKET_DEVICE))
+                devices |= ATA_ATAPI_MASTER;
+    }
+
+    /* ATAPI SLAVE */
+    if (mask & 0x02) {
+        if (!ata_device_select(ch, ATA_SLAVE))
+            if (!identify_device(ch, ATA_SLAVE, ATA_IDENTIFY_PACKET_DEVICE))
+                devices |= ATA_ATAPI_SLAVE;
     }
 
     if (bootverbose)
-	ata_printf(ch, -1, "reset tp3 devices=0x%b\n", ch->devices,
-		   "\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
-#endif
+        ata_printf(ch, -1, "reset check ATAPI: devices=%02x\n", devices);
+
+    if (!((devices & ATA_ATA_MASTER) && (devices & ATA_ATAPI_MASTER)))
+        ch->devices |= (devices & ATA_MASTER_MASK);
+
+    if (!((devices & ATA_ATA_SLAVE) && (devices & ATA_ATAPI_SLAVE)))
+        ch->devices |= (devices & ATA_SLAVE_MASK);
+
+    if (bootverbose)
+        ata_printf(ch, -1, "reset check final: devices=%02x\n", ch->devices);
+
+    return;
 }
 
 static int

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20031016231747.4f0640e3.eyes>