aboutsummaryrefslogtreecommitdiff
path: root/drivers/ata/sata_mv.c
diff options
context:
space:
mode:
authorMark Lord <liml@rtr.ca>2008-05-02 02:09:14 -0400
committerJeff Garzik <jgarzik@redhat.com>2008-05-06 11:37:39 -0400
commit9b2c4e0bae854fb5e88c9cacc0dacf21631c5cb0 (patch)
tree52f4be46f543e22c35d43806ad38764c165e9b3d /drivers/ata/sata_mv.c
parent616d4a98ad8749ebe17a8fcac67df65c321350ac (diff)
sata_mv wait for empty+idle
When performing EH, it is recommended to wait for the EDMA engine to empty out requests-in-progress before disabling EDMA. Introduce code to poll the EDMA_STATUS register for idle/empty bits before disabling EDMA. For non-EH operation, this will normally exit without delay, other than the register read. A later series of patches may focus on eliminating this and various other register reads (when possible) throughout the driver, but for now we're focussing on solid reliablity. Signed-off-by: Mark Lord <mlord@pobox.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata/sata_mv.c')
-rw-r--r--drivers/ata/sata_mv.c20
1 files changed, 20 insertions, 0 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 10ef9683f04..692996216b1 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -888,6 +888,25 @@ static void mv_start_dma(struct ata_port *ap, void __iomem *port_mmio,
}
}
+static void mv_wait_for_edma_empty_idle(struct ata_port *ap)
+{
+ void __iomem *port_mmio = mv_ap_base(ap);
+ const u32 empty_idle = (EDMA_STATUS_CACHE_EMPTY | EDMA_STATUS_IDLE);
+ const int per_loop = 5, timeout = (15 * 1000 / per_loop);
+ int i;
+
+ /*
+ * Wait for the EDMA engine to finish transactions in progress.
+ */
+ for (i = 0; i < timeout; ++i) {
+ u32 edma_stat = readl(port_mmio + EDMA_STATUS_OFS);
+ if ((edma_stat & empty_idle) == empty_idle)
+ break;
+ udelay(per_loop);
+ }
+ /* ata_port_printk(ap, KERN_INFO, "%s: %u+ usecs\n", __func__, i); */
+}
+
/**
* mv_stop_edma_engine - Disable eDMA engine
* @port_mmio: io base address
@@ -920,6 +939,7 @@ static int mv_stop_edma(struct ata_port *ap)
if (!(pp->pp_flags & MV_PP_FLAG_EDMA_EN))
return 0;
pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN;
+ mv_wait_for_edma_empty_idle(ap);
if (mv_stop_edma_engine(port_mmio)) {
ata_port_printk(ap, KERN_ERR, "Unable to stop eDMA\n");
return -EIO;