Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+11 −12
Original line number Diff line number Diff line
@@ -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);