summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2010-06-06 14:09:48 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2010-06-06 14:09:48 +0000
commit5327c141d1c7ccbdf07e0bb0178a617bf073478b (patch)
tree8d15ee13aba6f1b5587fe05b81e00d17f1851923 /sys/dev/ata
parent1a0d1f33835b644df56baa59f295df363e168874 (diff)
downloadFreeBSD-src-5327c141d1c7ccbdf07e0bb0178a617bf073478b.zip
FreeBSD-src-5327c141d1c7ccbdf07e0bb0178a617bf073478b.tar.gz
Some revisions of the Serverworks K2 SATA controller have a data
corruption bug where if an ATA command is issued before DMA is started, data will become available to the controller before it knows what to do with it. This results in either data corruption or a controller crash. This patch remedies the problem by adopting the workaround employed by Linux and Darwin: starting the DMA engine prior to sending the ATA command. Observer on: Xserve G5 Reviewed by: mav MFC after: 1 week
Diffstat (limited to 'sys/dev/ata')
-rw-r--r--sys/dev/ata/ata-all.h1
-rw-r--r--sys/dev/ata/ata-lowlevel.c11
-rw-r--r--sys/dev/ata/chipsets/ata-serverworks.c10
3 files changed, 21 insertions, 1 deletions
diff --git a/sys/dev/ata/ata-all.h b/sys/dev/ata/ata-all.h
index d7b3f44..336f0e3 100644
--- a/sys/dev/ata/ata-all.h
+++ b/sys/dev/ata/ata-all.h
@@ -564,6 +564,7 @@ struct ata_channel {
#define ATA_CHECKS_CABLE 0x20
#define ATA_NO_ATAPI_DMA 0x40
#define ATA_SATA 0x80
+#define ATA_DMA_BEFORE_CMD 0x100
int pm_level; /* power management level */
int devices; /* what is present */
diff --git a/sys/dev/ata/ata-lowlevel.c b/sys/dev/ata/ata-lowlevel.c
index 566b8f6..6c196d6 100644
--- a/sys/dev/ata/ata-lowlevel.c
+++ b/sys/dev/ata/ata-lowlevel.c
@@ -141,6 +141,14 @@ ata_begin_transaction(struct ata_request *request)
goto begin_finished;
}
+ /* start DMA engine if necessary */
+ if ((ch->flags & ATA_DMA_BEFORE_CMD) &&
+ ch->dma.start && ch->dma.start(request)) {
+ device_printf(request->parent, "error starting DMA\n");
+ request->result = EIO;
+ goto begin_finished;
+ }
+
/* issue command */
if (ch->hw.command(request)) {
device_printf(request->parent, "error issuing %s command\n",
@@ -150,7 +158,8 @@ ata_begin_transaction(struct ata_request *request)
}
/* start DMA engine */
- if (ch->dma.start && ch->dma.start(request)) {
+ if (!(ch->flags & ATA_DMA_BEFORE_CMD) &&
+ ch->dma.start && ch->dma.start(request)) {
device_printf(request->parent, "error starting DMA\n");
request->result = EIO;
goto begin_finished;
diff --git a/sys/dev/ata/chipsets/ata-serverworks.c b/sys/dev/ata/chipsets/ata-serverworks.c
index 29718ec..cee56e3 100644
--- a/sys/dev/ata/chipsets/ata-serverworks.c
+++ b/sys/dev/ata/chipsets/ata-serverworks.c
@@ -241,6 +241,16 @@ ata_serverworks_ch_attach(device_t dev)
ATA_OUTL(ctlr->r_res2, ch_offset + 0x88, 0);
ATA_OUTL(ctlr->r_res2, ch_offset + 0x80,
ATA_INL(ctlr->r_res2, ch_offset + 0x80) & ~0x00040000);
+
+ /*
+ * Some controllers have a bug where they will send the command
+ * to the drive before seeing a DMA start, and then can begin
+ * receiving data before the DMA start arrives. The controller
+ * will then become confused and either corrupt the data or crash.
+ * Remedy this by starting DMA before sending the drive command.
+ */
+
+ ch->flags |= ATA_DMA_BEFORE_CMD;
}
/* chip does not reliably do 64K DMA transfers */
OpenPOWER on IntegriCloud