Skip to content

Commit

Permalink
Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/git/jgarzik/libata-dev

* 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/libata-dev:
  libata-sff: fix spurious IRQ handling
  pata_via: Add VIA VX900 support
  • Loading branch information
torvalds committed Mar 24, 2010
2 parents 541e40e + 332ac7f commit c02c873
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 7 deletions.
43 changes: 36 additions & 7 deletions drivers/ata/libata-sff.c
Expand Up @@ -1667,6 +1667,7 @@ unsigned int ata_sff_host_intr(struct ata_port *ap,
{
struct ata_eh_info *ehi = &ap->link.eh_info;
u8 status, host_stat = 0;
bool bmdma_stopped = false;

VPRINTK("ata%u: protocol %d task_state %d\n",
ap->print_id, qc->tf.protocol, ap->hsm_task_state);
Expand Down Expand Up @@ -1699,6 +1700,7 @@ unsigned int ata_sff_host_intr(struct ata_port *ap,

/* before we do anything else, clear DMA-Start bit */
ap->ops->bmdma_stop(qc);
bmdma_stopped = true;

if (unlikely(host_stat & ATA_DMA_ERR)) {
/* error when transfering data to/from memory */
Expand All @@ -1716,8 +1718,14 @@ unsigned int ata_sff_host_intr(struct ata_port *ap,

/* check main status, clearing INTRQ if needed */
status = ata_sff_irq_status(ap);
if (status & ATA_BUSY)
goto idle_irq;
if (status & ATA_BUSY) {
if (bmdma_stopped) {
/* BMDMA engine is already stopped, we're screwed */
qc->err_mask |= AC_ERR_HSM;
ap->hsm_task_state = HSM_ST_ERR;
} else
goto idle_irq;
}

/* ack bmdma irq events */
ap->ops->sff_irq_clear(ap);
Expand Down Expand Up @@ -1762,13 +1770,16 @@ EXPORT_SYMBOL_GPL(ata_sff_host_intr);
irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
bool retried = false;
unsigned int i;
unsigned int handled = 0, polling = 0;
unsigned int handled, idle, polling;
unsigned long flags;

/* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
spin_lock_irqsave(&host->lock, flags);

retry:
handled = idle = polling = 0;
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
struct ata_queued_cmd *qc;
Expand All @@ -1782,15 +1793,18 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
handled |= ata_sff_host_intr(ap, qc);
else
polling |= 1 << i;
}
} else
idle |= 1 << i;
}

/*
* If no port was expecting IRQ but the controller is actually
* asserting IRQ line, nobody cared will ensue. Check IRQ
* pending status if available and clear spurious IRQ.
*/
if (!handled) {
if (!handled && !retried) {
bool retry = false;

for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];

Expand All @@ -1805,8 +1819,23 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
ata_port_printk(ap, KERN_INFO,
"clearing spurious IRQ\n");

ap->ops->sff_check_status(ap);
ap->ops->sff_irq_clear(ap);
if (idle & (1 << i)) {
ap->ops->sff_check_status(ap);
ap->ops->sff_irq_clear(ap);
} else {
/* clear INTRQ and check if BUSY cleared */
if (!(ap->ops->sff_check_status(ap) & ATA_BUSY))
retry |= true;
/*
* With command in flight, we can't do
* sff_irq_clear() w/o racing with completion.
*/
}
}

if (retry) {
retried = true;
goto retry;
}
}

Expand Down
1 change: 1 addition & 0 deletions drivers/ata/pata_via.c
Expand Up @@ -677,6 +677,7 @@ static const struct pci_device_id via[] = {
{ PCI_VDEVICE(VIA, 0x3164), },
{ PCI_VDEVICE(VIA, 0x5324), },
{ PCI_VDEVICE(VIA, 0xC409), VIA_IDFLAG_SINGLE },
{ PCI_VDEVICE(VIA, 0x9001), VIA_IDFLAG_SINGLE },

{ },
};
Expand Down

0 comments on commit c02c873

Please sign in to comment.