149 lines
4.3 KiB
Diff
149 lines
4.3 KiB
Diff
From: Tejun Heo <tj@kernel.org>
|
|
Subject: libata,ata_piix: detect and clear spurious IRQs
|
|
Patch-Mainline: Backported from changes pending for 2.6.34
|
|
References: bnc#445872, bnc#589449
|
|
|
|
Backport spurious IRQ handling from 2.6.34. It isn't exactly the same
|
|
form in that it doesn't use callbacks but implements custom
|
|
piix_interrupt() but should function the same. This protects ata_piix
|
|
against nobody-cared which gets reported not so rarely.
|
|
|
|
Signed-off-by: Tejun Heo <tj@kernel.org>
|
|
Signed-off-by: Tejun Heo <teheo@suse.de>
|
|
---
|
|
drivers/ata/ata_piix.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++-
|
|
drivers/ata/libata-sff.c | 12 ++++++-
|
|
2 files changed, 85 insertions(+), 3 deletions(-)
|
|
|
|
Index: linux-2.6.32-SLE11-SP1/drivers/ata/ata_piix.c
|
|
===================================================================
|
|
--- linux-2.6.32-SLE11-SP1.orig/drivers/ata/ata_piix.c
|
|
+++ linux-2.6.32-SLE11-SP1/drivers/ata/ata_piix.c
|
|
@@ -949,6 +949,80 @@ static int piix_sidpr_scr_read(struct at
|
|
return 0;
|
|
}
|
|
|
|
+static irqreturn_t piix_interrupt(int irq, void *dev_instance)
|
|
+{
|
|
+ struct ata_host *host = dev_instance;
|
|
+ bool retried = false;
|
|
+ unsigned int i;
|
|
+ unsigned int handled = 0, polling = 0, idle = 0;
|
|
+ unsigned long flags;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ if (ata_port_is_dummy(ap))
|
|
+ continue;
|
|
+
|
|
+ qc = ata_qc_from_tag(ap, ap->link.active_tag);
|
|
+ if (qc) {
|
|
+ if (!(qc->tf.flags & ATA_TFLAG_POLLING))
|
|
+ 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 && !retried) {
|
|
+ bool retry = false;
|
|
+
|
|
+ for (i = 0; i < host->n_ports; i++) {
|
|
+ struct ata_port *ap = host->ports[i];
|
|
+ u8 host_stat;
|
|
+
|
|
+ if (polling & (1 << i))
|
|
+ continue;
|
|
+
|
|
+ if (unlikely(!ap->ioaddr.bmdma_addr))
|
|
+ continue;
|
|
+
|
|
+ host_stat = ap->ops->bmdma_status(ap);
|
|
+ if (!(host_stat & ATA_DMA_INTR))
|
|
+ continue;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&host->lock, flags);
|
|
+
|
|
+ return IRQ_RETVAL(handled);
|
|
+}
|
|
+
|
|
static int piix_sidpr_scr_write(struct ata_link *link,
|
|
unsigned int reg, u32 val)
|
|
{
|
|
@@ -1607,7 +1681,7 @@ static int __devinit piix_init_one(struc
|
|
host->flags |= ATA_HOST_PARALLEL_SCAN;
|
|
|
|
pci_set_master(pdev);
|
|
- return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht);
|
|
+ return ata_pci_sff_activate_host(host, piix_interrupt, &piix_sht);
|
|
}
|
|
|
|
static void piix_remove_one(struct pci_dev *pdev)
|
|
Index: linux-2.6.32-SLE11-SP1/drivers/ata/libata-sff.c
|
|
===================================================================
|
|
--- linux-2.6.32-SLE11-SP1.orig/drivers/ata/libata-sff.c
|
|
+++ linux-2.6.32-SLE11-SP1/drivers/ata/libata-sff.c
|
|
@@ -1667,6 +1667,7 @@ unsigned int ata_sff_host_intr(struct at
|
|
{
|
|
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);
|
|
@@ -1699,6 +1700,7 @@ unsigned int ata_sff_host_intr(struct at
|
|
|
|
/* 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 */
|
|
@@ -1716,8 +1718,14 @@ unsigned int ata_sff_host_intr(struct at
|
|
|
|
/* 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);
|