aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-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 d7b3f4412d60..336f0e3966e0 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 566b8f676a59..6c196d60a442 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 29718ec5e48b..cee56e3681ee 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 */