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: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: Tejun Heo <tj@kernel.org> Cc: stable@vger.kernel.org
This commit is contained in:
Родитель
fcce9a35f8
Коммит
52a2a1087b
|
@ -619,17 +619,16 @@ static struct ata_port_operations sata_rcar_port_ops = {
|
|||
.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 ata_eh_info *ehi = &ap->link.eh_info;
|
||||
int freeze = 0;
|
||||
int handled = 0;
|
||||
u32 serror;
|
||||
|
||||
serror = ioread32(priv->base + SCRSERR_REG);
|
||||
if (!serror)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
DPRINTK("SError @host_intr: 0x%x\n", serror);
|
||||
|
||||
|
@ -642,7 +641,6 @@ static int sata_rcar_serr_interrupt(struct ata_port *ap)
|
|||
ata_ehi_push_desc(ehi, "%s", "hotplug");
|
||||
|
||||
freeze = serror & SERR_COMM_WAKE ? 0 : 1;
|
||||
handled = 1;
|
||||
}
|
||||
|
||||
/* freeze or abort */
|
||||
|
@ -650,11 +648,9 @@ static int sata_rcar_serr_interrupt(struct ata_port *ap)
|
|||
ata_port_freeze(ap);
|
||||
else
|
||||
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;
|
||||
int handled = 0;
|
||||
|
@ -663,7 +659,9 @@ static int sata_rcar_ata_interrupt(struct ata_port *ap)
|
|||
if (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)
|
||||
|
@ -678,20 +676,21 @@ static irqreturn_t sata_rcar_interrupt(int irq, void *dev_instance)
|
|||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
sataintstat = ioread32(priv->base + SATAINTSTAT_REG);
|
||||
sataintstat &= SATA_RCAR_INT_MASK;
|
||||
if (!sataintstat)
|
||||
goto done;
|
||||
/* ack */
|
||||
iowrite32(sataintstat & ~SATA_RCAR_INT_MASK,
|
||||
priv->base + SATAINTSTAT_REG);
|
||||
iowrite32(~sataintstat & 0x7ff, priv->base + SATAINTSTAT_REG);
|
||||
|
||||
ap = host->ports[0];
|
||||
|
||||
if (sataintstat & SATAINTSTAT_ATA)
|
||||
handled |= sata_rcar_ata_interrupt(ap);
|
||||
sata_rcar_ata_interrupt(ap);
|
||||
|
||||
if (sataintstat & SATAINTSTAT_SERR)
|
||||
handled |= sata_rcar_serr_interrupt(ap);
|
||||
sata_rcar_serr_interrupt(ap);
|
||||
|
||||
handled = 1;
|
||||
done:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче