Patch RFC: Promise SATA300 TX4 hardware bug workaround.

Alexander Sabourenkov screwdriver at lxnt.info
Thu Nov 1 16:07:18 PDT 2007


Hello.

I have ported the workaround for the hardware bug that causes data
corruption on Promise SATA300 TX4 cards to RELENG_7.

Bug description:
SATA300 TX4 hardware chokes if last PRD entry (in a dma transfer) is
larger than 164 bytes. This was found while analysing vendor-supplied
linux driver.

Workaround:
Split trailing PRD entry if it's larger that 164 bytes.

Two supplied patches do fix problem on my machine.

There is, however, a style problem with them. It seems like PRD entry
count is limited at 256. I have not found a good way to guarantee that
one entry is always available to do the split, thus the ugly solution of
patching ata-dma.c.


Patches, patched and original files are at http://lxnt.info/tx4/freebsd/.


--- ata-chipset.c.orig	2007-11-02 01:05:49.000000000 +0300
+++ ata-chipset.c	2007-11-02 01:05:49.000000000 +0300
@@ -142,6 +142,7 @@
 static int ata_promise_mio_command(struct ata_request *request);
 static void ata_promise_mio_reset(device_t dev);
 static void ata_promise_mio_dmainit(device_t dev);
+static void ata_promise_mio_dmasetprd(void *xsc, bus_dma_segment_t
*segs, int nsegs, int error);
 static void ata_promise_mio_setmode(device_t dev, int mode);
 static void ata_promise_sx4_intr(void *data);
 static int ata_promise_sx4_command(struct ata_request *request);
@@ -185,7 +186,6 @@
 static int ata_check_80pin(device_t dev, int mode);
 static int ata_mode2idx(int mode);

-
 /*
  * generic ATA support functions
  */
@@ -3759,8 +3759,44 @@
 static void
 ata_promise_mio_dmainit(device_t dev)
 {
+    struct ata_channel *ch = device_get_softc(dev);
+	
     /* note start and stop are not used here */
     ata_dmainit(dev);
+
+    if (ch->dma)
+	ch->dma->setprd = ata_promise_mio_dmasetprd;
+}
+
+static void
+ata_promise_mio_dmasetprd(void *xsc, bus_dma_segment_t *segs, int
nsegs, int error)
+{
+    #define PDC_MAXLASTSGSIZE 41*4
+    struct ata_dmasetprd_args *args = xsc;
+    struct ata_dma_prdentry *prd = args->dmatab;
+    int i;
+
+    if ((args->error = error))
+	return;
+
+    for (i = 0; i < nsegs; i++) {
+	prd[i].addr = htole32(segs[i].ds_addr);
+	prd[i].count = htole32(segs[i].ds_len);
+    }
+
+    if (segs[i - 1].ds_len > PDC_MAXLASTSGSIZE) {
+	/*
+	printf("splitting trailing PRD of %ld (limit %d)\n", segs[i -
1].ds_len, PDC_MAXLASTSGSIZE);
+	*/
+	prd[i - 1].count = htole32(segs[i - 1].ds_len - PDC_MAXLASTSGSIZE);
+	prd[i].count = htole32(PDC_MAXLASTSGSIZE);
+	prd[i].addr = htole32(segs[i - 1].ds_addr + PDC_MAXLASTSGSIZE);
+	i ++;
+	nsegs ++;
+    }
+
+    prd[i - 1].count |= htole32(ATA_DMA_EOT);
+    args->nsegs = nsegs;
 }

 static void

--- ata-dma.c.orig	2007-11-02 01:05:53.000000000 +0300
+++ ata-dma.c	2007-11-02 01:05:53.000000000 +0300
@@ -113,7 +113,7 @@
     if
(bus_dma_tag_create(ch->dma->dmatag,ch->dma->alignment,ch->dma->boundary,
 			   ch->dma->max_address, BUS_SPACE_MAXADDR,
 			   NULL, NULL, ch->dma->max_iosize,
-			   ATA_DMA_ENTRIES, ch->dma->segsize,
+			   ATA_DMA_ENTRIES - 1, ch->dma->segsize,
 			   0, NULL, NULL, &ch->dma->data_tag))
 	goto error;


-- 

./lxnt


More information about the freebsd-current mailing list