Commit 52a2a108 authored by Sergei Shtylyov's avatar Sergei Shtylyov Committed by Tejun Heo

sata_rcar: fix interrupt handling

The driver's interrupt handling code is too picky in deciding whether it should
handle an interrupt or not which causes completely unneeded spurious interrupts.
Thus make sata_rcar_{ata|serr}_interrupt() *void*; add ATA status register read
to sata_rcar_ata_interrupt() to clear an unexpected ATA interrupt -- it doesn't
get cleared by writing to the SATAINTSTAT register in the interrupt mode we use.

Also, in sata_rcar_ata_interrupt() we should check SATAINTSTAT register only for
enabled interrupts and we should clear  only those interrupts  that we have read
as active first time around, because else we have  a  race and risk clearing  an
interrupt that  can  occur between read  and write of the  SATAINTSTAT  register
and never registering it...
Signed-off-by: default avatarSergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Cc: stable@vger.kernel.org
parent fcce9a35
...@@ -619,17 +619,16 @@ static struct ata_port_operations sata_rcar_port_ops = { ...@@ -619,17 +619,16 @@ static struct ata_port_operations sata_rcar_port_ops = {
.bmdma_status = sata_rcar_bmdma_status, .bmdma_status = sata_rcar_bmdma_status,
}; };
static int sata_rcar_serr_interrupt(struct ata_port *ap) static void sata_rcar_serr_interrupt(struct ata_port *ap)
{ {
struct sata_rcar_priv *priv = ap->host->private_data; struct sata_rcar_priv *priv = ap->host->private_data;
struct ata_eh_info *ehi = &ap->link.eh_info; struct ata_eh_info *ehi = &ap->link.eh_info;
int freeze = 0; int freeze = 0;
int handled = 0;
u32 serror; u32 serror;
serror = ioread32(priv->base + SCRSERR_REG); serror = ioread32(priv->base + SCRSERR_REG);
if (!serror) if (!serror)
return 0; return;
DPRINTK("SError @host_intr: 0x%x\n", serror); DPRINTK("SError @host_intr: 0x%x\n", serror);
...@@ -642,7 +641,6 @@ static int sata_rcar_serr_interrupt(struct ata_port *ap) ...@@ -642,7 +641,6 @@ static int sata_rcar_serr_interrupt(struct ata_port *ap)
ata_ehi_push_desc(ehi, "%s", "hotplug"); ata_ehi_push_desc(ehi, "%s", "hotplug");
freeze = serror & SERR_COMM_WAKE ? 0 : 1; freeze = serror & SERR_COMM_WAKE ? 0 : 1;
handled = 1;
} }
/* freeze or abort */ /* freeze or abort */
...@@ -650,11 +648,9 @@ static int sata_rcar_serr_interrupt(struct ata_port *ap) ...@@ -650,11 +648,9 @@ static int sata_rcar_serr_interrupt(struct ata_port *ap)
ata_port_freeze(ap); ata_port_freeze(ap);
else else
ata_port_abort(ap); ata_port_abort(ap);
return handled;
} }
static int sata_rcar_ata_interrupt(struct ata_port *ap) static void sata_rcar_ata_interrupt(struct ata_port *ap)
{ {
struct ata_queued_cmd *qc; struct ata_queued_cmd *qc;
int handled = 0; int handled = 0;
...@@ -663,7 +659,9 @@ static int sata_rcar_ata_interrupt(struct ata_port *ap) ...@@ -663,7 +659,9 @@ static int sata_rcar_ata_interrupt(struct ata_port *ap)
if (qc) if (qc)
handled |= ata_bmdma_port_intr(ap, qc); handled |= ata_bmdma_port_intr(ap, qc);
return handled; /* be sure to clear ATA interrupt */
if (!handled)
sata_rcar_check_status(ap);
} }
static irqreturn_t sata_rcar_interrupt(int irq, void *dev_instance) static irqreturn_t sata_rcar_interrupt(int irq, void *dev_instance)
...@@ -678,20 +676,21 @@ static irqreturn_t sata_rcar_interrupt(int irq, void *dev_instance) ...@@ -678,20 +676,21 @@ static irqreturn_t sata_rcar_interrupt(int irq, void *dev_instance)
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
sataintstat = ioread32(priv->base + SATAINTSTAT_REG); sataintstat = ioread32(priv->base + SATAINTSTAT_REG);
sataintstat &= SATA_RCAR_INT_MASK;
if (!sataintstat) if (!sataintstat)
goto done; goto done;
/* ack */ /* ack */
iowrite32(sataintstat & ~SATA_RCAR_INT_MASK, iowrite32(~sataintstat & 0x7ff, priv->base + SATAINTSTAT_REG);
priv->base + SATAINTSTAT_REG);
ap = host->ports[0]; ap = host->ports[0];
if (sataintstat & SATAINTSTAT_ATA) if (sataintstat & SATAINTSTAT_ATA)
handled |= sata_rcar_ata_interrupt(ap); sata_rcar_ata_interrupt(ap);
if (sataintstat & SATAINTSTAT_SERR) if (sataintstat & SATAINTSTAT_SERR)
handled |= sata_rcar_serr_interrupt(ap); sata_rcar_serr_interrupt(ap);
handled = 1;
done: done:
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
......
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