Merge "spmi: qpnp-int: call arbiter ops only when all peripheral irqs are disabled"
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
index eedb1e5..03e9021 100644
--- a/drivers/spmi/qpnp-int.c
+++ b/drivers/spmi/qpnp-int.c
@@ -167,21 +167,20 @@
 		pr_err_ratelimited("%s: decode failed on hwirq %lu\n",
 							__func__, d->hwirq);
 		return rc;
-	} else {
-		if (irq_d->priv_d == QPNPINT_INVALID_DATA) {
-			rc = chip_d->cb->register_priv_data(chip_d->spmi_ctrl,
-						&q_spec, &irq_d->priv_d);
-			if (rc) {
-				pr_err_ratelimited(
-					"%s: decode failed on hwirq %lu\n",
-					__func__, d->hwirq);
-				return rc;
-			}
-
-		}
-		arb_op(chip_d->spmi_ctrl, &q_spec, irq_d->priv_d);
 	}
 
+	if (irq_d->priv_d == QPNPINT_INVALID_DATA) {
+		rc = chip_d->cb->register_priv_data(chip_d->spmi_ctrl,
+					&q_spec, &irq_d->priv_d);
+		if (rc) {
+			pr_err_ratelimited(
+				"%s: decode failed on hwirq %lu rc = %d\n",
+				__func__, d->hwirq, rc);
+			return rc;
+		}
+	}
+	arb_op(chip_d->spmi_ctrl, &q_spec, irq_d->priv_d);
+
 	return 0;
 }
 
@@ -191,6 +190,7 @@
 	struct q_chip_data *chip_d = irq_d->chip_d;
 	struct q_perip_data *per_d = irq_d->per_d;
 	int rc;
+	uint8_t prev_int_en = per_d->int_en;
 
 	pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
 
@@ -201,10 +201,16 @@
 		return;
 	}
 
-	qpnpint_arbiter_op(d, irq_d, chip_d->cb->mask);
-
 	per_d->int_en &= ~irq_d->mask_shift;
 
+	if (prev_int_en && !(per_d->int_en)) {
+		/*
+		 * no interrupt on this peripheral is enabled
+		 * ask the arbiter to ignore this peripheral
+		 */
+		qpnpint_arbiter_op(d, irq_d, chip_d->cb->mask);
+	}
+
 	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
 					(u8 *)&irq_d->mask_shift, 1);
 	if (rc) {
@@ -221,6 +227,7 @@
 	struct q_chip_data *chip_d = irq_d->chip_d;
 	struct q_perip_data *per_d = irq_d->per_d;
 	int rc;
+	uint8_t prev_int_en = per_d->int_en;
 
 	pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
 
@@ -231,10 +238,16 @@
 		return;
 	}
 
-	qpnpint_arbiter_op(d, irq_d, chip_d->cb->mask);
-
 	per_d->int_en &= ~irq_d->mask_shift;
 
+	if (prev_int_en && !(per_d->int_en)) {
+		/*
+		 * no interrupt on this peripheral is enabled
+		 * ask the arbiter to ignore this peripheral
+		 */
+		qpnpint_arbiter_op(d, irq_d, chip_d->cb->mask);
+	}
+
 	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
 							&irq_d->mask_shift, 1);
 	if (rc) {
@@ -256,6 +269,7 @@
 	struct q_chip_data *chip_d = irq_d->chip_d;
 	struct q_perip_data *per_d = irq_d->per_d;
 	int rc;
+	uint8_t prev_int_en = per_d->int_en;
 
 	pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
 
@@ -266,9 +280,15 @@
 		return;
 	}
 
-	qpnpint_arbiter_op(d, irq_d, chip_d->cb->unmask);
-
 	per_d->int_en |= irq_d->mask_shift;
+	if (!prev_int_en && per_d->int_en) {
+		/*
+		 * no interrupt prior to this call was enabled for the
+		 * peripheral. Ask the arbiter to enable interrupts for
+		 * this peripheral
+		 */
+		qpnpint_arbiter_op(d, irq_d, chip_d->cb->unmask);
+	}
 	rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_SET,
 					&irq_d->mask_shift, 1);
 	if (rc) {
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 05a4806..f85a576 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -101,6 +101,9 @@
 #define PMIC_ARB_APID_MASK		0xFF
 #define PMIC_ARB_PPID_MASK		0xFFF
 
+/* interrupt enable bit */
+#define SPMI_PIC_ACC_ENABLE_BIT		BIT(0)
+
 /**
  * base - base address of the PMIC Arbiter core registers.
  * intr - base address of the SPMI interrupt control registers
@@ -434,8 +437,10 @@
 
 	spin_lock_irqsave(&pmic_arb->lock, flags);
 	status = readl_relaxed(pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
-	if (!status) {
-		writel_relaxed(0x1, pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) {
+		status = status | SPMI_PIC_ACC_ENABLE_BIT;
+		writel_relaxed(status,
+				pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
 		/* Interrupt needs to be enabled before returning to caller */
 		wmb();
 	}
@@ -467,8 +472,11 @@
 
 	spin_lock_irqsave(&pmic_arb->lock, flags);
 	status = readl_relaxed(pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
-	if (status) {
-		writel_relaxed(0x0, pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (status & SPMI_PIC_ACC_ENABLE_BIT) {
+		/* clear the enable bit and write */
+		status = status & ~SPMI_PIC_ACC_ENABLE_BIT;
+		writel_relaxed(status,
+				pmic_arb->intr + SPMI_PIC_ACC_ENABLE(apid));
 		/* Interrupt needs to be disabled before returning to caller */
 		wmb();
 	}
@@ -480,7 +488,7 @@
 periph_interrupt(struct spmi_pmic_arb_dev *pmic_arb, u8 apid)
 {
 	u16 ppid = get_peripheral_id(pmic_arb, apid);
-	void __iomem *base = pmic_arb->intr;
+	void __iomem *intr = pmic_arb->intr;
 	u8 sid = (ppid >> 8) & 0x0F;
 	u8 pid = ppid & 0xFF;
 	u32 status;
@@ -491,11 +499,20 @@
 		/* return IRQ_NONE; */
 	}
 
+	status = readl_relaxed(intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) {
+		/*
+		 * All interrupts from this peripheral are disabled
+		 * don't bother calling the qpnpint handler
+		 */
+		return IRQ_HANDLED;
+	}
+
 	/* Read the peripheral specific interrupt bits */
-	status = readl_relaxed(base + SPMI_PIC_IRQ_STATUS(apid));
+	status = readl_relaxed(intr + SPMI_PIC_IRQ_STATUS(apid));
 
 	/* Clear the peripheral interrupts */
-	writel_relaxed(status, base + SPMI_PIC_IRQ_CLEAR(apid));
+	writel_relaxed(status, intr + SPMI_PIC_IRQ_CLEAR(apid));
 	/* Interrupt needs to be cleared/acknowledged before exiting ISR */
 	mb();