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