Commit 299b3f8d authored by Mark Lord's avatar Mark Lord Committed by Jeff Garzik

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: default avatarMark Lord <mlord@pobox.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 8d2b450d
...@@ -1881,6 +1881,39 @@ static u8 mv_bmdma_status(struct ata_port *ap) ...@@ -1881,6 +1881,39 @@ static u8 mv_bmdma_status(struct ata_port *ap)
return status; 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. * mv_qc_prep - Host specific command preparation.
* @qc: queued command to prepare * @qc: queued command to prepare
...@@ -1902,9 +1935,16 @@ static void mv_qc_prep(struct ata_queued_cmd *qc) ...@@ -1902,9 +1935,16 @@ static void mv_qc_prep(struct ata_queued_cmd *qc)
u16 flags = 0; u16 flags = 0;
unsigned in_index; unsigned in_index;
if ((tf->protocol != ATA_PROT_DMA) && switch (tf->protocol) {
(tf->protocol != ATA_PROT_NCQ)) case ATA_PROT_DMA:
case ATA_PROT_NCQ:
break; /* continue below */
case ATA_PROT_PIO:
mv_rw_multi_errata_sata24(qc);
return; return;
default:
return;
}
/* Fill in command request block /* Fill in command request block
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment