summaryrefslogtreecommitdiff
path: root/drivers/ata
diff options
context:
space:
mode:
authorMark Lord <liml@rtr.ca>2009-04-13 11:29:34 -0400
committerJeff Garzik <jgarzik@redhat.com>2009-04-17 19:04:28 -0400
commit299b3f8df90a3f7416d8df121d8a42b1a2aeced4 (patch)
tree600bd461c66e9980c3dc3554148fb594e5805f5f /drivers/ata
parent8d2b450d0f9233f221d545f26720eebbc468e857 (diff)
downloadlinux-3.10-299b3f8df90a3f7416d8df121d8a42b1a2aeced4.tar.gz
linux-3.10-299b3f8df90a3f7416d8df121d8a42b1a2aeced4.tar.bz2
linux-3.10-299b3f8df90a3f7416d8df121d8a42b1a2aeced4.zip
sata_mv: workaround for multi_count errata sata24
Workaround for errata SATA#24 in sata_mv. This errata affects WRITE_MULTI* commands when the device multi_count produces a DRQ block size >= 4Kbytes. We work around it here by converting such operations into ordinary PIO_WRITEs instead. Note that this might result in a PIO FUA write unavoidably being converted into a non-FUA write. In practice, any system using FUA is also going to be using DMA rather than PIO, so this shouldn't affect anyone in the real world. Signed-off-by: Mark Lord <mlord@pobox.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/sata_mv.c44
1 files changed, 42 insertions, 2 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index c7da6022c6b..870dcfd8235 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -1881,6 +1881,39 @@ static u8 mv_bmdma_status(struct ata_port *ap)
return status;
}
+static void mv_rw_multi_errata_sata24(struct ata_queued_cmd *qc)
+{
+ struct ata_taskfile *tf = &qc->tf;
+ /*
+ * Workaround for 88SX60x1 FEr SATA#24.
+ *
+ * Chip may corrupt WRITEs if multi_count >= 4kB.
+ * Note that READs are unaffected.
+ *
+ * It's not clear if this errata really means "4K bytes",
+ * or if it always happens for multi_count > 7
+ * regardless of device sector_size.
+ *
+ * So, for safety, any write with multi_count > 7
+ * gets converted here into a regular PIO write instead:
+ */
+ if ((tf->flags & ATA_TFLAG_WRITE) && is_multi_taskfile(tf)) {
+ if (qc->dev->multi_count > 7) {
+ switch (tf->command) {
+ case ATA_CMD_WRITE_MULTI:
+ tf->command = ATA_CMD_PIO_WRITE;
+ break;
+ case ATA_CMD_WRITE_MULTI_FUA_EXT:
+ tf->flags &= ~ATA_TFLAG_FUA; /* ugh */
+ /* fall through */
+ case ATA_CMD_WRITE_MULTI_EXT:
+ tf->command = ATA_CMD_PIO_WRITE_EXT;
+ break;
+ }
+ }
+ }
+}
+
/**
* mv_qc_prep - Host specific command preparation.
* @qc: queued command to prepare
@@ -1902,9 +1935,16 @@ static void mv_qc_prep(struct ata_queued_cmd *qc)
u16 flags = 0;
unsigned in_index;
- if ((tf->protocol != ATA_PROT_DMA) &&
- (tf->protocol != ATA_PROT_NCQ))
+ switch (tf->protocol) {
+ case ATA_PROT_DMA:
+ case ATA_PROT_NCQ:
+ break; /* continue below */
+ case ATA_PROT_PIO:
+ mv_rw_multi_errata_sata24(qc);
return;
+ default:
+ return;
+ }
/* Fill in command request block
*/