Commit f740d168 authored by Robert Hancock's avatar Robert Hancock Committed by Jeff Garzik

sata_nv: don't rely on NV_INT_DEV indication with ADMA

Several people reported issues with certain drive commands timing out on
sata_nv controllers running in ADMA mode. The commands in question were
non-DMA-mapped commands, usually FLUSH CACHE or FLUSH CACHE EXT.

 From experimentation it appears that the NV_INT_DEV indication isn't
always set when a legitimate command completion interrupt is received on
a legacy-mode command, at least not on these controllers in ADMA mode.
When a command is pending on the port, force the flag on always in the
irq_stat value before calling nv_host_intr so that the drive busy state
is always checked by ata_host_intr.

This also fixes some questionable code in nv_host_intr which called
ata_check_status when a command was pending and ata_host_intr returned
"unhandled". If the device interrupted at just the wrong time this could
cause interrupts to be lost.
Signed-off-by: default avatarRobert Hancock <hancockr@shaw.ca>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 82490c09
...@@ -700,7 +700,6 @@ static void nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err) ...@@ -700,7 +700,6 @@ static void nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err)
static int nv_host_intr(struct ata_port *ap, u8 irq_stat) static int nv_host_intr(struct ata_port *ap, u8 irq_stat)
{ {
struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
int handled;
/* freeze if hotplugged */ /* freeze if hotplugged */
if (unlikely(irq_stat & (NV_INT_ADDED | NV_INT_REMOVED))) { if (unlikely(irq_stat & (NV_INT_ADDED | NV_INT_REMOVED))) {
...@@ -719,13 +718,7 @@ static int nv_host_intr(struct ata_port *ap, u8 irq_stat) ...@@ -719,13 +718,7 @@ static int nv_host_intr(struct ata_port *ap, u8 irq_stat)
} }
/* handle interrupt */ /* handle interrupt */
handled = ata_host_intr(ap, qc); return ata_host_intr(ap, qc);
if (unlikely(!handled)) {
/* spurious, clear it */
ata_check_status(ap);
}
return 1;
} }
static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance) static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
...@@ -752,6 +745,11 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance) ...@@ -752,6 +745,11 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) { if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
u8 irq_stat = readb(host->mmio_base + NV_INT_STATUS_CK804) u8 irq_stat = readb(host->mmio_base + NV_INT_STATUS_CK804)
>> (NV_INT_PORT_SHIFT * i); >> (NV_INT_PORT_SHIFT * i);
if(ata_tag_valid(ap->active_tag))
/** NV_INT_DEV indication seems unreliable at times
at least in ADMA mode. Force it on always when a
command is active, to prevent losing interrupts. */
irq_stat |= NV_INT_DEV;
handled += nv_host_intr(ap, irq_stat); handled += nv_host_intr(ap, irq_stat);
continue; continue;
} }
......
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