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:
Sergei Shtylyov 2013-06-01 02:38:35 +04:00 коммит произвёл Tejun Heo
Родитель fcce9a35f8
Коммит 52a2a1087b
1 изменённых файлов: 11 добавлений и 12 удалений

Просмотреть файл

@ -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);