diff options
-rw-r--r-- | sys/dev/ata/ata-all.h | 1 | ||||
-rw-r--r-- | sys/dev/ata/ata-lowlevel.c | 11 | ||||
-rw-r--r-- | sys/dev/ata/chipsets/ata-serverworks.c | 10 |
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 */ |