diff options
author | Mark Lord <liml@rtr.ca> | 2009-04-13 11:29:34 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2009-04-17 19:04:28 -0400 |
commit | 299b3f8df90a3f7416d8df121d8a42b1a2aeced4 (patch) | |
tree | 600bd461c66e9980c3dc3554148fb594e5805f5f /drivers/ata | |
parent | 8d2b450d0f9233f221d545f26720eebbc468e857 (diff) |
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.c | 44 |
1 files changed, 42 insertions, 2 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index c7da6022c6b4..870dcfd82357 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) | |||
1881 | return status; | 1881 | return status; |
1882 | } | 1882 | } |
1883 | 1883 | ||
1884 | static void mv_rw_multi_errata_sata24(struct ata_queued_cmd *qc) | ||
1885 | { | ||
1886 | struct ata_taskfile *tf = &qc->tf; | ||
1887 | /* | ||
1888 | * Workaround for 88SX60x1 FEr SATA#24. | ||
1889 | * | ||
1890 | * Chip may corrupt WRITEs if multi_count >= 4kB. | ||
1891 | * Note that READs are unaffected. | ||
1892 | * | ||
1893 | * It's not clear if this errata really means "4K bytes", | ||
1894 | * or if it always happens for multi_count > 7 | ||
1895 | * regardless of device sector_size. | ||
1896 | * | ||
1897 | * So, for safety, any write with multi_count > 7 | ||
1898 | * gets converted here into a regular PIO write instead: | ||
1899 | */ | ||
1900 | if ((tf->flags & ATA_TFLAG_WRITE) && is_multi_taskfile(tf)) { | ||
1901 | if (qc->dev->multi_count > 7) { | ||
1902 | switch (tf->command) { | ||
1903 | case ATA_CMD_WRITE_MULTI: | ||
1904 | tf->command = ATA_CMD_PIO_WRITE; | ||
1905 | break; | ||
1906 | case ATA_CMD_WRITE_MULTI_FUA_EXT: | ||
1907 | tf->flags &= ~ATA_TFLAG_FUA; /* ugh */ | ||
1908 | /* fall through */ | ||
1909 | case ATA_CMD_WRITE_MULTI_EXT: | ||
1910 | tf->command = ATA_CMD_PIO_WRITE_EXT; | ||
1911 | break; | ||
1912 | } | ||
1913 | } | ||
1914 | } | ||
1915 | } | ||
1916 | |||
1884 | /** | 1917 | /** |
1885 | * mv_qc_prep - Host specific command preparation. | 1918 | * mv_qc_prep - Host specific command preparation. |
1886 | * @qc: queued command to prepare | 1919 | * @qc: queued command to prepare |
@@ -1902,9 +1935,16 @@ static void mv_qc_prep(struct ata_queued_cmd *qc) | |||
1902 | u16 flags = 0; | 1935 | u16 flags = 0; |
1903 | unsigned in_index; | 1936 | unsigned in_index; |
1904 | 1937 | ||
1905 | if ((tf->protocol != ATA_PROT_DMA) && | 1938 | switch (tf->protocol) { |
1906 | (tf->protocol != ATA_PROT_NCQ)) | 1939 | case ATA_PROT_DMA: |
1940 | case ATA_PROT_NCQ: | ||
1941 | break; /* continue below */ | ||
1942 | case ATA_PROT_PIO: | ||
1943 | mv_rw_multi_errata_sata24(qc); | ||
1907 | return; | 1944 | return; |
1945 | default: | ||
1946 | return; | ||
1947 | } | ||
1908 | 1948 | ||
1909 | /* Fill in command request block | 1949 | /* Fill in command request block |
1910 | */ | 1950 | */ |