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: Robert Hancock <hancockr@shaw.ca> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
82490c0937
commit
f740d1689d
1 changed files with 6 additions and 8 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue