kern/111084: [ata] [patch] No support for ahci atapi sata cd/dvd drives

Richard Burton burtons at mosquitonet.com
Sun Apr 1 06:00:13 UTC 2007


>Number:         111084
>Category:       kern
>Synopsis:       [ata] [patch] No support for ahci atapi sata cd/dvd drives
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Apr 01 06:00:12 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Richard Burton
>Release:        6.2
>Organization:
>Environment:
FreeBSD bsdloon.local.net 6.2-RELEASE FreeBSD 6.2-RELEASE #17: Mon Mar 26 12:09:28 AKDT 2007     root at bsdloon.local.net:/usr/obj/usr/src/sys/GENERIC  i386
>Description:
atapi sata drives attached to an intel ich8 controller are not recognized
or supported by the freebsd-6.2 ata driver.  This was not obvious (at
least to me) from the install documentation, but it is obvious when
looking at the ata driver source code.

The attached patch provides at least basic support for atapi sata cd and
dvd drives attached to the ahci intel ich8 controller on my machine.
I can read data cds and dvds, write data cds using burncd,
and (with atapicam loaded) write cds and dvds using cdrecord and growisofs.

Here is dmesg output relevent to the hardware I have created this patch
for:

atapci0: <Intel ICH8 SATA300 controller> port 0xfe00-0xfe07,0xfe10-0xfe13,0xfe20
-0xfe27,0xfe30-0xfe33,0xfec0-0xfedf mem 0xff970000-0xff9707ff irq 20 at device 3
1.2 on pci0
atapci0: Reserved 0x20 bytes for rid 0x20 type 4 at 0xfec0
ioapic0: routing intpin 20 (PCI IRQ 20) to vector 55
atapci0: [MPSAFE]
atapci0: Reserved 0x800 bytes for rid 0x24 type 3 at 0xff970000
atapci0: AHCI Version 01.10 controller with 6 ports detected
ata2: <ATA channel 0> on atapci0
ata2: SATA connect ready time=0ms
ata2: sata_connect devices=0x1<ATA_MASTER>
ata2: [MPSAFE]
ata3: <ATA channel 1> on atapci0
ata3: SATA connect ready time=0ms
ata3: sata_connect devices=0x4<ATAPI_MASTER>
ata3: [MPSAFE]
ata4: <ATA channel 2> on atapci0
ata4: SATA connect ready time=0ms
ata4: sata_connect devices=0x4<ATAPI_MASTER>
ata4: [MPSAFE]
ata5: <ATA channel 3> on atapci0
ata5: SATA connect ready time=0ms
ata5: sata_connect devices=0x1<ATA_MASTER>
ata5: [MPSAFE]
ata6: <ATA channel 4> on atapci0
ata6: SATA connect status=00000004
ata6: [MPSAFE]
ata7: <ATA channel 5> on atapci0
ata7: SATA connect status=00000004
ata7: [MPSAFE]

..

ata2-master: pio=PIO4 wdma=WDMA2 udma=UDMA133 cable=40 wire
ad4: 152587MB <Seagate ST3160812AS 3.ADH> at ata2-master SATA300
ad4: 312500000 sectors [310019C/16H/63S] 16 sectors/interrupt 1 depth queue
GEOM: new disk ad4
ad4: Intel check1 failed
ad4: Adaptec check1 failed
ad4: LSI (v3) check1 failed
ad4: LSI (v2) check1 failed
ad4: FreeBSD check1 failed
ata3-master: pio=PIO4 wdma=WDMA2 udma=UDMA100 cable=40 wire
acd0: <PHILIPS DVD-ROM DROM6316/OD2F> DVDROM drive at ata3 as master
acd0: read 1KB/s (8251KB/s), 512KB buffer, SATA150
acd0: Reads: CDR, CDRW, CDDA stream, DVDROM, DVDR, packet
acd0: Writes:
acd0: Audio: play, 256 volume levels
acd0: Mechanism: ejectable tray, unlocked
acd0: Medium: no/blank disc
ata4-master: pio=PIO4 wdma=WDMA2 udma=UDMA33 cable=40 wire
acd1: <TSSTcorp DVD+/-RW TS-H553A/DE04> DVDR drive at ata4 as master
acd1: read 8269KB/s (8269KB/s) write 8269KB/s (8269KB/s), 2048KB buffer, SATA150
acd1: Reads: CDR, CDRW, CDDA stream, DVDROM, DVDR, packet
acd1: Writes: CDR, CDRW, DVDR, test write, burnproof
acd1: Audio: play, 256 volume levels
acd1: Mechanism: ejectable tray, unlocked
acd1: Medium: no/blank disc
ata5-master: pio=PIO4 wdma=WDMA2 udma=UDMA133 cable=40 wire
ad10: 476940MB <Seagate ST3500641AS 3.ADG> at ata5-master SATA300
ad10: 976773168 sectors [969021C/16H/63S] 16 sectors/interrupt 1 depth queue
ad10: Intel check1 failed
GEOM: new disk ad10
ad10: Adaptec check1 failed
ad10: LSI (v3) check1 failed
ad10: LSI (v2) check1 failed
ad10: FreeBSD check1 failed

This patch is to the 6.2-RELEASE source.  Sorry, I don't have a
FreeBsd-Current system installed.  I can see that some of these changes
(i.e. the atapi fis structure) are included in a slightly different
form there.  However, other changes that I found necessary do not seem to
be included yet.
>How-To-Repeat:

>Fix:


Patch attached with submission follows:

--- dev/ata/ata-chipset.c.orig	Mon Oct  9 08:01:35 2006
+++ dev/ata/ata-chipset.c	Mon Mar 26 12:03:11 2007
@@ -68,6 +68,8 @@
 static void ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
 static void ata_ahci_dmainit(device_t dev);
 static int ata_ahci_setup_fis(u_int8_t *fis, struct ata_request *request);
+/* rgb add engine restart */
+static void ata_ahci_restart_engine(device_t dev);
 static int ata_acard_chipinit(device_t dev);
 static int ata_acard_allocate(device_t dev);
 static int ata_acard_status(device_t dev);
@@ -452,7 +454,7 @@
 
     /* setup legacy cruft we need */
     ch->r_io[ATA_CYL_LSB].res = ctlr->r_res2;
-    ch->r_io[ATA_CYL_LSB].offset = ATA_AHCI_P_SIG + 1 + offset;
+    ch->r_io[ATA_CYL_LSB].offset = ATA_AHCI_P_SIG + 2 + offset;
     ch->r_io[ATA_CYL_MSB].res = ctlr->r_res2;
     ch->r_io[ATA_CYL_MSB].offset = ATA_AHCI_P_SIG + 3 + offset;
     ch->r_io[ATA_STATUS].res = ctlr->r_res2;
@@ -493,7 +495,14 @@
 	      ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
 
     /* start operations on this channel */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+    /* rgb - add ATAPI cmd */
+    if (ch->devices & ATA_ATAPI_MASTER)
+      ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+             (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
+              ATA_AHCI_P_CMD_ATAPI |
+              ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+    else
+      ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
 	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
 	      ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
     return 0;
@@ -506,6 +515,9 @@
     struct ata_channel *ch = device_get_softc(dev);
     struct ata_connect_task *tp;
     u_int32_t action, istatus, sstatus, error, issued;
+    /* rgb - add tfd_data */
+    u_int32_t tf_data;
+    /* end rgb additions */
     int offset = (ch->unit << 7);
     int tag = 0;
 
@@ -558,8 +570,24 @@
 	}
 
 	/* do we have any device action ? */
-	if (!(issued & (1 << tag)))
+	if (!(issued & (1 << tag))) 
 	    return 1;
+
+	/* rgb - check for error - This is necessary for atapi
+		code to work, and it seems to also prevent
+		occasional disk timeouts that sometimes cause
+		boot to fail
+        */
+         tf_data = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset);
+         if (tf_data & ATA_S_ERROR) {
+		if ((tf_data & ATA_S_BUSY) || (tf_data & ATA_S_DRQ))
+	        	return 0;   /* for now, let timeout do reset */
+		else {
+			/* restart engine to clear ATA_AHCI_P_CI */
+			ata_ahci_restart_engine(ch->dev);
+	 	        return 1;
+		}
+         }
     }
     return 0;
 }
@@ -579,6 +607,14 @@
     ctp = (struct ata_ahci_cmd_tab *)
 	  (ch->dma->work + ATA_AHCI_CT_OFFSET + (ATA_AHCI_CT_SIZE * tag));
 
+    /* rgb - if ATAPI request moves data, make it dma */
+    if (request->flags & ATA_R_ATAPI) {
+	if (request->bytecount && !(request->flags & ATA_R_READ))
+		request->flags |= ATA_R_WRITE;
+	if (request->flags & (ATA_R_READ | ATA_R_WRITE))
+		request->flags |= ATA_R_DMA;
+    }
+
     /* setup the FIS for this request */ /* XXX SOS ATAPI missing still */
     if (!(fis_size = ata_ahci_setup_fis(&ctp->cfis[0], request))) {
 	device_printf(request->dev, "setting up SATA FIS failed\n");
@@ -604,10 +640,17 @@
     clp->prd_length = entries;
     clp->cmd_flags = (request->flags & ATA_R_WRITE ? (1<<6) : 0) |
 		     (request->flags & ATA_R_ATAPI ? (1<<5) : 0) |
+/*  rgb - set prefetch flag if ATAPI */
+		     (request->flags & ATA_R_ATAPI ? (1<<7) : 0) |
 		     (fis_size / sizeof(u_int32_t));
     clp->bytecount = 0;
     clp->cmd_table_phys = htole64(ch->dma->work_bus + ATA_AHCI_CT_OFFSET +
 				  (ATA_AHCI_CT_SIZE * tag));
+    /* rgb - add atapi request */
+    if (request->flags & ATA_R_ATAPI) {
+          bzero(ctp->acmd, 32);
+          bcopy(request->u.atapi.ccb, ctp->acmd, 16);
+    }
 
     /* clear eventual ACTIVE bit */
     ATA_IDX_OUTL(ch, ATA_SACTIVE, ATA_IDX_INL(ch, ATA_SACTIVE) & (1 << tag));
@@ -615,6 +658,28 @@
     /* issue the command */
     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CI + (ch->unit << 7), (1 << tag));
 
+    /* rgb - handle ATA_DEVICE_RESET requests */
+    if (!(request->flags & ATA_R_ATAPI)) {
+	if (request->u.ata.command == ATA_DEVICE_RESET) {
+		u_int32_t tf_data;
+		int timeout = 1000000;
+		do {
+  		   DELAY(10);
+		   tf_data = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + (ch->unit << 7));
+		} while (tf_data & ATA_S_BUSY && timeout--);
+		request->status = tf_data;
+		if (timeout <= 0) {
+		    device_printf(request->dev, "ATA_DEVICE_RESET timeout\n");
+		    /* should probably do softreset */
+		}
+		if (request->status & ATA_S_ERROR) {
+			request->error = tf_data >> 8;
+			ata_ahci_restart_engine(ch->dev);
+		}
+		return ATA_OP_FINISHED;
+	}
+    }
+
     /* start the timeout */
     callout_reset(&request->callout, request->timeout * hz,
 		  (timeout_t*)ata_timeout, request);
@@ -653,6 +718,36 @@
     return ATA_OP_FINISHED;
 }
 
+/* rgb add ahci_restart_engine */
+static void
+ata_ahci_restart_engine(device_t dev)
+{
+    struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+    struct ata_channel *ch = device_get_softc(dev);
+    int tmp, offset = (ch->unit <<7); 
+    int timeout = 0;
+
+    /*  stop engine  */
+    tmp = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+    tmp &= ~ATA_AHCI_P_CMD_ST;
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, tmp);
+	    do {
+		DELAY (1000);
+		if (timeout++ >500) {
+		    device_printf(ch->dev, "stopping AHCI engine failed\n");
+		    break;
+		}
+	    }
+            while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &
+			ATA_AHCI_P_CMD_CR);
+
+    /*  restart engine  */
+    tmp = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+    tmp |= ATA_AHCI_P_CMD_ST;
+    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, tmp);
+}
+ 
+
 static void
 ata_ahci_reset(device_t dev)
 {
@@ -701,7 +796,14 @@
 	     ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
 
     /* start operations on this channel */
-    ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+    /* rgb - add ATAPI cmd */
+    if (ch->devices & ATA_ATAPI_MASTER)
+      ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
+	      ATA_AHCI_P_CMD_ATAPI |
+	      ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+    else
+      ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
 	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
 	      ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
 }
@@ -746,6 +848,11 @@
 
     fis[idx++] = 0x27;  /* host to device */
     fis[idx++] = 0x80;  /* command FIS (note PM goes here) */
+
+    /* rgb - add atapi request */
+    switch (request->flags & ATA_R_ATAPI) {
+
+    default:
     fis[idx++] = request->u.ata.command;
     fis[idx++] = request->u.ata.feature;
 
@@ -772,6 +879,40 @@
     fis[idx++] = 0x00;
     fis[idx++] = 0x00;
     fis[idx++] = 0x00;
+    break;
+
+    case ATA_R_ATAPI:
+    fis[idx++] = ATA_PACKET_CMD;
+    if (request->flags & ATA_R_DMA) {
+	fis[idx++] = ATA_F_DMA;
+	fis[idx++] = 0x00;
+	fis[idx++] = 0x00;
+	fis[idx++] = 0x00;
+    }
+    else {
+	fis[idx++] = 0x00;
+	fis[idx++] = 0x00;
+	fis[idx++] = request->transfersize;
+	fis[idx++] = request->transfersize >> 8;
+    }
+    fis[idx++] = atadev->unit;
+
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = ATA_A_4BIT;
+
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    fis[idx++] = 0x00;
+    break;
+    } 
+
     return idx;
 }
 
--- dev/ata/atapi-cd.c.orig	Sat Sep  2 09:01:32 2006
+++ dev/ata/atapi-cd.c	Wed Mar 21 20:48:42 2007
@@ -699,7 +699,10 @@
 	request->dev = dev;
 	bcopy(ccb, request->u.atapi.ccb, 16);
 	request->flags = ATA_R_ATAPI;
+	/* rgb - up timeout to 10 
 	request->timeout = 5;
+	*/
+	request->timeout = 10;
 	ata_queue_request(request);
 	if (!request->error &&
 	    (request->u.atapi.sense.key == 2 ||
@@ -1234,7 +1237,10 @@
     request->flags = ATA_R_ATAPI | ATA_R_READ;
     request->timeout = 30;
     ata_queue_request(request);
+/*  rgb - fix for pr 109270 from Martin Birgmeier
     if (!request->error && request->u.atapi.sense.error & ATA_SENSE_VALID)
+*/
+    if (!request->error && request->u.atapi.sense.specific & ATA_SENSE_SPEC_VALID)
 	*finished = ((request->u.atapi.sense.specific2 |
 		     (request->u.atapi.sense.specific1 << 8)) * 100) / 65535;
     else

>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list