[PATCH 6/7] spmi: apple: use IRQ for RX FIFO if possible

From: Sasha Finkelstein

Date: Sat Jul 25 2026 - 09:51:42 EST


From: Alba Mendez <me@xxxxxxx>

The IRQ fires as soon as the reply is available, which is usually takes
a few us instead of the 10ms sleep interval for polling

Signed-off-by: Alba Mendez <me@xxxxxxx>
Signed-off-by: Sasha Finkelstein <k@xxxxxxxxxxxxxx>
---
drivers/spmi/spmi-apple-controller.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 96 insertions(+), 1 deletion(-)

diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
index d8509f027df6..d627187b3ac0 100644
--- a/drivers/spmi/spmi-apple-controller.c
+++ b/drivers/spmi/spmi-apple-controller.c
@@ -11,6 +11,8 @@
* spmi-pmic-arb.c Copyright (c) 2021, The Linux Foundation.
*/

+#include <linux/completion.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
@@ -23,6 +25,12 @@
#define SPMI_CMD_REG 0x4
#define SPMI_RSP_REG 0x8

+#define SPMI_IRQ_MASK_BASE 0x20
+#define SPMI_IRQ_ACK_BASE 0x60
+#define SPMI_IRQ_USER_SIZE 0x20
+
+#define SPMI_IRQ_FIFO_RX 0
+
/* SPMI_RSP_REG reply word */
#define SPMI_REPLY_FRAME_PARITY_OFFSET 16
#define SPMI_REPLY_ACK BIT(15)
@@ -37,6 +45,8 @@
struct apple_spmi {
void __iomem *regs;
struct mutex fifo_lock;
+ bool fifo_rx_irq;
+ struct completion fifo_rx;
};

#define poll_reg(spmi, reg, val, cond) \
@@ -55,7 +65,19 @@ static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl)
int ret;
u32 status;

- ret = poll_reg(spmi, SPMI_STATUS_REG, status, !(status & SPMI_RX_FIFO_EMPTY));
+ if (spmi->fifo_rx_irq) {
+ ret = wait_for_completion_timeout(&spmi->fifo_rx,
+ usecs_to_jiffies(REG_POLL_TIMEOUT_US));
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)
+ ret = -EIO;
+ else
+ ret = 0;
+ } else {
+ ret = poll_reg(spmi, SPMI_STATUS_REG, status, !(status & SPMI_RX_FIFO_EMPTY));
+ }
+
if (ret) {
dev_err(&ctrl->dev,
"failed to wait for RX FIFO not empty\n");
@@ -79,6 +101,8 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
if (ret)
return ret;

+ reinit_completion(&spmi->fifo_rx);
+
writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);

while (i < len) {
@@ -183,10 +207,68 @@ static int spmi_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
return -EINVAL;
}

+static void apple_spmi_irq_ack_raw(struct apple_spmi *spmi, u32 irq)
+{
+ u32 __iomem *reg = spmi->regs + SPMI_IRQ_ACK_BASE + (irq / 32) * 4;
+
+ writel(BIT(irq % 32), reg);
+}
+
+static void apple_spmi_irq_mask_raw(struct apple_spmi *spmi, u32 irq)
+{
+ u32 __iomem *reg = spmi->regs + SPMI_IRQ_MASK_BASE + (irq / 32) * 4;
+
+ writel(readl(reg) & ~BIT(irq % 32), reg);
+}
+
+static void apple_spmi_irq_unmask_raw(struct apple_spmi *spmi, u32 irq)
+{
+ u32 __iomem *reg = spmi->regs + SPMI_IRQ_MASK_BASE + (irq / 32) * 4;
+
+ writel(readl(reg) | BIT(irq % 32), reg);
+}
+
+static irqreturn_t apple_spmi_irq_handler(int irq, void *dev_id)
+{
+ struct apple_spmi *spmi = dev_id;
+ bool handled = false;
+ u32 val, offset, bit;
+
+ val = readl(spmi->regs + SPMI_IRQ_ACK_BASE + SPMI_IRQ_USER_SIZE);
+ if (val & BIT(SPMI_IRQ_FIFO_RX)) {
+ complete(&spmi->fifo_rx);
+ apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_USER_SIZE * 8 + SPMI_IRQ_FIFO_RX);
+ handled = true;
+ }
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int apple_spmi_init_irq(struct platform_device *pdev,
+ struct apple_spmi *spmi, int irq)
+{
+ int ret;
+
+ for (size_t offset = 0; offset < SPMI_IRQ_USER_SIZE + 4; offset += 4) {
+ writel(0, spmi->regs + SPMI_IRQ_MASK_BASE + offset);
+ writel(U32_MAX, spmi->regs + SPMI_IRQ_ACK_BASE + offset);
+ }
+
+ spmi->fifo_rx_irq = true;
+ apple_spmi_irq_unmask_raw(spmi, SPMI_IRQ_USER_SIZE * 8 + SPMI_IRQ_FIFO_RX);
+
+ ret = devm_request_any_context_irq(&pdev->dev, irq, apple_spmi_irq_handler, 0, NULL, spmi);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "failed to request IRQ\n");
+
+ return 0;
+}
+
static int apple_spmi_probe(struct platform_device *pdev)
{
struct apple_spmi *spmi;
struct spmi_controller *ctrl;
+ int irq;
int ret;

ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi));
@@ -195,6 +277,7 @@ static int apple_spmi_probe(struct platform_device *pdev)

spmi = spmi_controller_get_drvdata(ctrl);
mutex_init(&spmi->fifo_lock);
+ init_completion(&spmi->fifo_rx);

spmi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(spmi->regs))
@@ -206,11 +289,23 @@ static int apple_spmi_probe(struct platform_device *pdev)
ctrl->write_cmd = spmi_write_cmd;
ctrl->cmd = spmi_cmd;

+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq < 0 && irq != -ENXIO)
+ return irq;
+ if (irq >= 0) {
+ ret = apple_spmi_init_irq(pdev, spmi, irq);
+ if (ret)
+ return ret;
+ }
+
ret = devm_spmi_controller_add(&pdev->dev, ctrl);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"spmi_controller_add failed\n");

+ dev_dbg(&pdev->dev, irq >= 0 ? "Initialized with IRQ" :
+ "Initialized without IRQ, falling back to polling");
+
return 0;
}


--
2.55.0