Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/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
This commit is contained in:
@@ -1667,6 +1667,7 @@ unsigned int ata_sff_host_intr(struct ata_port *ap,
|
|||||||
{
|
{
|
||||||
struct ata_eh_info *ehi = &ap->link.eh_info;
|
struct ata_eh_info *ehi = &ap->link.eh_info;
|
||||||
u8 status, host_stat = 0;
|
u8 status, host_stat = 0;
|
||||||
|
bool bmdma_stopped = false;
|
||||||
|
|
||||||
VPRINTK("ata%u: protocol %d task_state %d\n",
|
VPRINTK("ata%u: protocol %d task_state %d\n",
|
||||||
ap->print_id, qc->tf.protocol, ap->hsm_task_state);
|
ap->print_id, qc->tf.protocol, ap->hsm_task_state);
|
||||||
@@ -1699,6 +1700,7 @@ unsigned int ata_sff_host_intr(struct ata_port *ap,
|
|||||||
|
|
||||||
/* before we do anything else, clear DMA-Start bit */
|
/* before we do anything else, clear DMA-Start bit */
|
||||||
ap->ops->bmdma_stop(qc);
|
ap->ops->bmdma_stop(qc);
|
||||||
|
bmdma_stopped = true;
|
||||||
|
|
||||||
if (unlikely(host_stat & ATA_DMA_ERR)) {
|
if (unlikely(host_stat & ATA_DMA_ERR)) {
|
||||||
/* error when transfering data to/from memory */
|
/* error when transfering data to/from memory */
|
||||||
@@ -1716,8 +1718,14 @@ unsigned int ata_sff_host_intr(struct ata_port *ap,
|
|||||||
|
|
||||||
/* check main status, clearing INTRQ if needed */
|
/* check main status, clearing INTRQ if needed */
|
||||||
status = ata_sff_irq_status(ap);
|
status = ata_sff_irq_status(ap);
|
||||||
if (status & ATA_BUSY)
|
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;
|
goto idle_irq;
|
||||||
|
}
|
||||||
|
|
||||||
/* ack bmdma irq events */
|
/* ack bmdma irq events */
|
||||||
ap->ops->sff_irq_clear(ap);
|
ap->ops->sff_irq_clear(ap);
|
||||||
@@ -1762,13 +1770,16 @@ EXPORT_SYMBOL_GPL(ata_sff_host_intr);
|
|||||||
irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
|
irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
|
||||||
{
|
{
|
||||||
struct ata_host *host = dev_instance;
|
struct ata_host *host = dev_instance;
|
||||||
|
bool retried = false;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
unsigned int handled = 0, polling = 0;
|
unsigned int handled, idle, polling;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
/* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
|
/* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
|
retry:
|
||||||
|
handled = idle = polling = 0;
|
||||||
for (i = 0; i < host->n_ports; i++) {
|
for (i = 0; i < host->n_ports; i++) {
|
||||||
struct ata_port *ap = host->ports[i];
|
struct ata_port *ap = host->ports[i];
|
||||||
struct ata_queued_cmd *qc;
|
struct ata_queued_cmd *qc;
|
||||||
@@ -1782,7 +1793,8 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
|
|||||||
handled |= ata_sff_host_intr(ap, qc);
|
handled |= ata_sff_host_intr(ap, qc);
|
||||||
else
|
else
|
||||||
polling |= 1 << i;
|
polling |= 1 << i;
|
||||||
}
|
} else
|
||||||
|
idle |= 1 << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1790,7 +1802,9 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
|
|||||||
* asserting IRQ line, nobody cared will ensue. Check IRQ
|
* asserting IRQ line, nobody cared will ensue. Check IRQ
|
||||||
* pending status if available and clear spurious 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++) {
|
for (i = 0; i < host->n_ports; i++) {
|
||||||
struct ata_port *ap = host->ports[i];
|
struct ata_port *ap = host->ports[i];
|
||||||
|
|
||||||
@@ -1805,8 +1819,23 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
|
|||||||
ata_port_printk(ap, KERN_INFO,
|
ata_port_printk(ap, KERN_INFO,
|
||||||
"clearing spurious IRQ\n");
|
"clearing spurious IRQ\n");
|
||||||
|
|
||||||
|
if (idle & (1 << i)) {
|
||||||
ap->ops->sff_check_status(ap);
|
ap->ops->sff_check_status(ap);
|
||||||
ap->ops->sff_irq_clear(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -677,6 +677,7 @@ static const struct pci_device_id via[] = {
|
|||||||
{ PCI_VDEVICE(VIA, 0x3164), },
|
{ PCI_VDEVICE(VIA, 0x3164), },
|
||||||
{ PCI_VDEVICE(VIA, 0x5324), },
|
{ PCI_VDEVICE(VIA, 0x5324), },
|
||||||
{ PCI_VDEVICE(VIA, 0xC409), VIA_IDFLAG_SINGLE },
|
{ PCI_VDEVICE(VIA, 0xC409), VIA_IDFLAG_SINGLE },
|
||||||
|
{ PCI_VDEVICE(VIA, 0x9001), VIA_IDFLAG_SINGLE },
|
||||||
|
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user