Devicetree
 help / color / mirror / Atom feed
* [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support.
@ 2026-08-05 10:11 Sasha Finkelstein
  2026-08-05 10:11 ` [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x Sasha Finkelstein
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 10:11 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: asahi, linux-arm-kernel, linux-kernel, devicetree,
	Sasha Finkelstein, Alba Mendez

The SPMI bus has grown additional responsibilities in the M3
generation, making the current driver insufficient. Add M3 comatibles,
support for slave-sent interrupts, FIFO interrupts, power management
commands, parity validation, and fix locking.

To simplify the merge strategy, the device tree entries will be sent
in a future patch series.

Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
Changes in v4:
- Re-do error recovery.
- Fix an address calculation mistake
- Link to v3: https://patch.msgid.link/20260803-t603x-spmi-v3-0-c17b506d91a1@chaosmail.tech

Changes in v3:
- Rework interrupt support
- Address review comments
- Link to v2: https://patch.msgid.link/20260728-t603x-spmi-v2-0-f43e5f10e583@chaosmail.tech

Changes in v2:
- Change locking to non-interruptible
- Reorder irq ack
- Clarify dt binding
- Some data type cleanups
- Link to v1: https://patch.msgid.link/20260725-t603x-spmi-v1-0-e1a29fcd2d38@chaosmail.tech

---
Alba Mendez (5):
      spmi: apple: Validate FIFO state
      spmi: apple: check transaction status
      spmi: apple: Implement remaining commands
      spmi: apple: lock around FIFOs
      spmi: apple: Add interrupt functionality

Sasha Finkelstein (1):
      dt-bindings: spmi: apple,spmi: Add t603x

 Documentation/devicetree/bindings/spmi/apple,spmi.yaml |  15 +++++
 drivers/spmi/Kconfig                                   |   3 +-
 drivers/spmi/spmi-apple-controller.c                   | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 371 insertions(+), 31 deletions(-)
---
base-commit: 0ce37745d4bfbc493f718169c3974898ffec8ee7
change-id: 20260725-t603x-spmi-74630bf1b0a0

Best regards,
--  
Sasha Finkelstein <k@chaosmail.tech>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x
  2026-08-05 10:11 [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support Sasha Finkelstein
@ 2026-08-05 10:11 ` Sasha Finkelstein
  2026-08-05 10:19   ` sashiko-bot
  2026-08-05 12:01   ` Janne Grunau
  2026-08-05 10:11 ` [PATCH v4 2/6] spmi: apple: Validate FIFO state Sasha Finkelstein
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 10:11 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: asahi, linux-arm-kernel, linux-kernel, devicetree,
	Sasha Finkelstein

Add t603x compatibles, interrupt support, and support for SPMI
controllers that are not always-on.

Keeping the "interrupt-controller" property as optional, as taking the
j514c as an example, we only need this functionality on 3 out of 10
SPMI controllers present on this SoC.

Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
 Documentation/devicetree/bindings/spmi/apple,spmi.yaml | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
index 3e5b14bc8c31..fceed47b2cd1 100644
--- a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
+++ b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
@@ -20,8 +20,11 @@ properties:
       - items:
           - enum:
               - apple,t6020-spmi
+              - apple,t6030-spmi
+              - apple,t6031-spmi
               - apple,t8012-spmi
               - apple,t8015-spmi
+              - apple,t8122-spmi
           - const: apple,t8103-spmi
       - items:
           - enum:
@@ -34,6 +37,18 @@ properties:
   reg:
     maxItems: 1
 
+  interrupts:
+    maxItems: 1
+    description: Optional, operates in polled mode if not present
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+
+  power-domains:
+    maxItems: 1
+
 required:
   - compatible
   - reg

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 2/6] spmi: apple: Validate FIFO state
  2026-08-05 10:11 [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support Sasha Finkelstein
  2026-08-05 10:11 ` [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x Sasha Finkelstein
@ 2026-08-05 10:11 ` Sasha Finkelstein
  2026-08-05 10:21   ` sashiko-bot
  2026-08-05 12:03   ` Janne Grunau
  2026-08-05 10:11 ` [PATCH v4 3/6] spmi: apple: check transaction status Sasha Finkelstein
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 10:11 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: asahi, linux-arm-kernel, linux-kernel, devicetree,
	Sasha Finkelstein, Alba Mendez

From: Alba Mendez <me@alba.sh>

Check for data before reading the body of a reply, and check for
end of data afterwards.

Signed-off-by: Alba Mendez <me@alba.sh>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
 drivers/spmi/spmi-apple-controller.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
index 376cf682c43e..4d5356bf9bbf 100644
--- a/drivers/spmi/spmi-apple-controller.c
+++ b/drivers/spmi/spmi-apple-controller.c
@@ -21,7 +21,9 @@
 #define SPMI_STATUS_REG 0
 #define SPMI_CMD_REG 0x4
 #define SPMI_RSP_REG 0x8
+#define SPMI_ACT_REG 0xa4
 
+#define SPMI_ACT_FIFO_FLUSH BIT(0)
 #define SPMI_RX_FIFO_EMPTY BIT(24)
 
 #define REG_POLL_INTERVAL_US 10000
@@ -29,6 +31,7 @@
 
 struct apple_spmi {
 	void __iomem *regs;
+	bool prev_fail;
 };
 
 #define poll_reg(spmi, reg, val, cond) \
@@ -49,6 +52,7 @@ static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl)
 
 	ret = poll_reg(spmi, SPMI_STATUS_REG, status, !(status & SPMI_RX_FIFO_EMPTY));
 	if (ret) {
+		spmi->prev_fail = true;
 		dev_err(&ctrl->dev,
 			"failed to wait for RX FIFO not empty\n");
 		return ret;
@@ -67,6 +71,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	u8 i;
 	int ret;
 
+	if (spmi->prev_fail) {
+		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
+		spmi->prev_fail = false;
+	}
+
 	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
 
 	ret = apple_spmi_wait_rx_not_empty(ctrl);
@@ -78,6 +87,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 
 	/* Read SPMI data reply */
 	while (len_read < len) {
+		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
+			dev_err_ratelimited(&ctrl->dev,
+					    "FIFO lacks reply data, controller stuck?\n");
+			return -EIO;
+		}
 		rsp = readl(spmi->regs + SPMI_RSP_REG);
 		i = 0;
 		while ((len_read < len) && (i < 4)) {
@@ -86,6 +100,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 		}
 	}
 
+	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
+		dev_warn(&ctrl->dev, "FIFO has extra data\n");
+		spmi->prev_fail = true;
+	}
+
 	return 0;
 }
 
@@ -97,6 +116,11 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	size_t i = 0, j;
 	int ret;
 
+	if (spmi->prev_fail) {
+		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
+		spmi->prev_fail = false;
+	}
+
 	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
 
 	while (i < len) {
@@ -115,6 +139,11 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	/* Discard */
 	readl(spmi->regs + SPMI_RSP_REG);
 
+	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
+		dev_warn(&ctrl->dev, "FIFO has extra data\n");
+		spmi->prev_fail = true;
+	}
+
 	return 0;
 }
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 3/6] spmi: apple: check transaction status
  2026-08-05 10:11 [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support Sasha Finkelstein
  2026-08-05 10:11 ` [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x Sasha Finkelstein
  2026-08-05 10:11 ` [PATCH v4 2/6] spmi: apple: Validate FIFO state Sasha Finkelstein
@ 2026-08-05 10:11 ` Sasha Finkelstein
  2026-08-05 10:27   ` sashiko-bot
  2026-08-05 12:06   ` Janne Grunau
  2026-08-05 10:11 ` [PATCH v4 4/6] spmi: apple: Implement remaining commands Sasha Finkelstein
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 10:11 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: asahi, linux-arm-kernel, linux-kernel, devicetree,
	Sasha Finkelstein, Alba Mendez

From: Alba Mendez <me@alba.sh>

Check for parity errors and missing command ACKs

Signed-off-by: Alba Mendez <me@alba.sh>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
 drivers/spmi/spmi-apple-controller.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
index 4d5356bf9bbf..7ebf899edf17 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/bitfield.h>
+#include <linux/bits.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
@@ -23,6 +25,12 @@
 #define SPMI_RSP_REG 0x8
 #define SPMI_ACT_REG 0xa4
 
+/* SPMI_RSP_REG reply word */
+#define SPMI_REPLY_FRAME_PARITY_STATUS GENMASK(31, 16)
+#define SPMI_REPLY_ACK BIT(15)
+#define SPMI_REPLY_SLAVE_ID GENMASK(14, 8)
+#define SPMI_REPLY_CMD GENMASK(7, 0)
+
 #define SPMI_ACT_FIFO_FLUSH BIT(0)
 #define SPMI_RX_FIFO_EMPTY BIT(24)
 
@@ -66,7 +74,7 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 {
 	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
 	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
-	u32 rsp;
+	u32 reply, rsp;
 	size_t len_read = 0;
 	u8 i;
 	int ret;
@@ -82,12 +90,12 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	if (ret)
 		return ret;
 
-	/* Discard SPMI reply status */
-	readl(spmi->regs + SPMI_RSP_REG);
+	reply = readl(spmi->regs + SPMI_RSP_REG);
 
 	/* Read SPMI data reply */
 	while (len_read < len) {
 		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
+			spmi->prev_fail = true;
 			dev_err_ratelimited(&ctrl->dev,
 					    "FIFO lacks reply data, controller stuck?\n");
 			return -EIO;
@@ -105,6 +113,10 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 		spmi->prev_fail = true;
 	}
 
+	if (~FIELD_GET(SPMI_REPLY_FRAME_PARITY_STATUS, reply) & ((1 << len) - 1)) {
+		dev_err(&ctrl->dev, "some frames failed parity check\n");
+		return -EIO;
+	}
 	return 0;
 }
 
@@ -113,6 +125,7 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 {
 	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
 	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
+	u32 reply;
 	size_t i = 0, j;
 	int ret;
 
@@ -136,14 +149,17 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	if (ret)
 		return ret;
 
-	/* Discard */
-	readl(spmi->regs + SPMI_RSP_REG);
+	reply = readl(spmi->regs + SPMI_RSP_REG);
 
 	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
 		dev_warn(&ctrl->dev, "FIFO has extra data\n");
 		spmi->prev_fail = true;
 	}
 
+	if (!FIELD_GET(SPMI_REPLY_ACK, reply)) {
+		dev_err(&ctrl->dev, "command not acknowledged\n");
+		return -EIO;
+	}
 	return 0;
 }
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 4/6] spmi: apple: Implement remaining commands
  2026-08-05 10:11 [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support Sasha Finkelstein
                   ` (2 preceding siblings ...)
  2026-08-05 10:11 ` [PATCH v4 3/6] spmi: apple: check transaction status Sasha Finkelstein
@ 2026-08-05 10:11 ` Sasha Finkelstein
  2026-08-05 10:24   ` sashiko-bot
  2026-08-05 12:19   ` Janne Grunau
  2026-08-05 10:11 ` [PATCH v4 5/6] spmi: apple: lock around FIFOs Sasha Finkelstein
  2026-08-05 10:11 ` [PATCH v4 6/6] spmi: apple: Add interrupt functionality Sasha Finkelstein
  5 siblings, 2 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 10:11 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: asahi, linux-arm-kernel, linux-kernel, devicetree,
	Sasha Finkelstein, Alba Mendez

From: Alba Mendez <me@alba.sh>

Add support for zero write and power management commands

Signed-off-by: Alba Mendez <me@alba.sh>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
 drivers/spmi/spmi-apple-controller.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------
 1 file changed, 68 insertions(+), 46 deletions(-)

diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
index 7ebf899edf17..49827e51e93e 100644
--- a/drivers/spmi/spmi-apple-controller.c
+++ b/drivers/spmi/spmi-apple-controller.c
@@ -46,9 +46,9 @@ struct apple_spmi {
 	readl_poll_timeout((spmi)->regs + (reg), (val), (cond), \
 			   REG_POLL_INTERVAL_US, REG_POLL_TIMEOUT_US)
 
-static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 saddr, size_t len)
+static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 param)
 {
-	return opc | sid << 8 | saddr << 16 | (len - 1) | (1 << 15);
+	return opc | sid << 8 | (u32)param << 16 | (1 << 15);
 }
 
 /* Wait for Rx FIFO to have something */
@@ -69,14 +69,14 @@ static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl)
 	return 0;
 }
 
-static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
-			 u16 saddr, u8 *buf, size_t len)
+static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
+			 u16 param, const u8 *buf, size_t len, u8 *ibuf, size_t ilen)
 {
 	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
-	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
+	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, param);
 	u32 reply, rsp;
 	size_t len_read = 0;
-	u8 i;
+	size_t i = 0, j;
 	int ret;
 
 	if (spmi->prev_fail) {
@@ -86,6 +86,14 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 
 	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
 
+	while (i < len) {
+		j = min_t(size_t, sizeof(spmi_cmd), len - i);
+		spmi_cmd = 0;
+		memcpy(&spmi_cmd, buf + i, j);
+		writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
+		i += j;
+	}
+
 	ret = apple_spmi_wait_rx_not_empty(ctrl);
 	if (ret)
 		return ret;
@@ -93,7 +101,7 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	reply = readl(spmi->regs + SPMI_RSP_REG);
 
 	/* Read SPMI data reply */
-	while (len_read < len) {
+	while (len_read < ilen) {
 		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
 			spmi->prev_fail = true;
 			dev_err_ratelimited(&ctrl->dev,
@@ -101,11 +109,9 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 			return -EIO;
 		}
 		rsp = readl(spmi->regs + SPMI_RSP_REG);
-		i = 0;
-		while ((len_read < len) && (i < 4)) {
-			buf[len_read++] = ((0xff << (8 * i)) & rsp) >> (8 * i);
-			i += 1;
-		}
+		i = min_t(size_t, sizeof(spmi_cmd), ilen - len_read);
+		memcpy(ibuf + len_read, &rsp, i);
+		len_read += i;
 	}
 
 	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
@@ -113,54 +119,69 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 		spmi->prev_fail = true;
 	}
 
-	if (~FIELD_GET(SPMI_REPLY_FRAME_PARITY_STATUS, reply) & ((1 << len) - 1)) {
+	if (!ilen && !FIELD_GET(SPMI_REPLY_ACK, reply)) {
+		dev_err(&ctrl->dev, "command not acknowledged\n");
+		return -EIO;
+	}
+	if (~FIELD_GET(SPMI_REPLY_FRAME_PARITY_STATUS, reply) & ((1 << ilen) - 1)) {
 		dev_err(&ctrl->dev, "some frames failed parity check\n");
 		return -EIO;
 	}
 	return 0;
 }
 
-static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
-			  u16 saddr, const u8 *buf, size_t len)
+/* Send a raw command with 1..16 input data frames */
+static int spmi_raw_cmd_input(struct spmi_controller *ctrl, u8 opc, u8 sid,
+			 u16 param, u8 *buf, size_t len)
 {
-	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
-	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
-	u32 reply;
-	size_t i = 0, j;
-	int ret;
-
-	if (spmi->prev_fail) {
-		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
-		spmi->prev_fail = false;
-	}
-
-	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
+	return spmi_raw_cmd(ctrl, opc, sid, param, NULL, 0, buf, len);
+}
 
-	while (i < len) {
-		j = 0;
-		spmi_cmd = 0;
-		while ((j < 4) & (i < len))
-			spmi_cmd |= buf[i++] << (j++ * 8);
+/* Send a raw command with (optional) body and an input ACK */
+static int spmi_raw_cmd_ack(struct spmi_controller *ctrl, u8 opc, u8 sid,
+			  u16 param, const u8 *buf, size_t len)
+{
+	return spmi_raw_cmd(ctrl, opc, sid, param, buf, len, NULL, 0);
+}
 
-		writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
+static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
+			 u16 saddr, u8 *buf, size_t len)
+{
+	switch (opc) {
+	case SPMI_CMD_EXT_READ:
+	case SPMI_CMD_EXT_READL:
+		return spmi_raw_cmd_input(ctrl, opc | (len - 1), sid, saddr, buf, len);
+	case SPMI_CMD_READ:
+		return spmi_raw_cmd_input(ctrl, opc | saddr, sid, saddr, buf, len);
 	}
+	return -EINVAL;
+}
 
-	ret = apple_spmi_wait_rx_not_empty(ctrl);
-	if (ret)
-		return ret;
-
-	reply = readl(spmi->regs + SPMI_RSP_REG);
-
-	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
-		dev_warn(&ctrl->dev, "FIFO has extra data\n");
-		spmi->prev_fail = true;
+static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
+			  u16 saddr, const u8 *buf, size_t len)
+{
+	switch (opc) {
+	case SPMI_CMD_WRITE:
+		return spmi_raw_cmd_ack(ctrl, opc | saddr, sid, buf[0] << 8 | saddr, NULL, 0);
+	case SPMI_CMD_ZERO_WRITE:
+		return spmi_raw_cmd_ack(ctrl, opc | buf[0], sid, buf[0] << 8 | saddr, NULL, 0);
+	case SPMI_CMD_EXT_WRITE:
+	case SPMI_CMD_EXT_WRITEL:
+		return spmi_raw_cmd_ack(ctrl, opc | (len - 1), sid, saddr, buf, len);
 	}
+	return -EINVAL;
+}
 
-	if (!FIELD_GET(SPMI_REPLY_ACK, reply)) {
-		dev_err(&ctrl->dev, "command not acknowledged\n");
-		return -EIO;
+static int spmi_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
+{
+	switch (opc) {
+	case SPMI_CMD_RESET:
+	case SPMI_CMD_SLEEP:
+	case SPMI_CMD_SHUTDOWN:
+	case SPMI_CMD_WAKEUP:
+		return spmi_raw_cmd_ack(ctrl, opc, sid, 0, NULL, 0);
 	}
-	return 0;
+	return -EINVAL;
 }
 
 static int apple_spmi_probe(struct platform_device *pdev)
@@ -183,6 +204,7 @@ static int apple_spmi_probe(struct platform_device *pdev)
 
 	ctrl->read_cmd = spmi_read_cmd;
 	ctrl->write_cmd = spmi_write_cmd;
+	ctrl->cmd = spmi_cmd;
 
 	ret = devm_spmi_controller_add(&pdev->dev, ctrl);
 	if (ret)

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 5/6] spmi: apple: lock around FIFOs
  2026-08-05 10:11 [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support Sasha Finkelstein
                   ` (3 preceding siblings ...)
  2026-08-05 10:11 ` [PATCH v4 4/6] spmi: apple: Implement remaining commands Sasha Finkelstein
@ 2026-08-05 10:11 ` Sasha Finkelstein
  2026-08-05 10:25   ` sashiko-bot
  2026-08-05 12:25   ` Janne Grunau
  2026-08-05 10:11 ` [PATCH v4 6/6] spmi: apple: Add interrupt functionality Sasha Finkelstein
  5 siblings, 2 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 10:11 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: asahi, linux-arm-kernel, linux-kernel, devicetree,
	Sasha Finkelstein, Alba Mendez

From: Alba Mendez <me@alba.sh>

The driver was missing locking around register interactions

Signed-off-by: Alba Mendez <me@alba.sh>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
 drivers/spmi/spmi-apple-controller.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
index 49827e51e93e..c3bc1f6d5741 100644
--- a/drivers/spmi/spmi-apple-controller.c
+++ b/drivers/spmi/spmi-apple-controller.c
@@ -16,6 +16,7 @@
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/spmi.h>
 
@@ -39,6 +40,7 @@
 
 struct apple_spmi {
 	void __iomem *regs;
+	struct mutex fifo_lock;
 	bool prev_fail;
 };
 
@@ -79,6 +81,8 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	size_t i = 0, j;
 	int ret;
 
+	guard(mutex)(&spmi->fifo_lock);
+
 	if (spmi->prev_fail) {
 		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
 		spmi->prev_fail = false;
@@ -195,6 +199,7 @@ static int apple_spmi_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	spmi = spmi_controller_get_drvdata(ctrl);
+	mutex_init(&spmi->fifo_lock);
 
 	spmi->regs = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(spmi->regs))

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4 6/6] spmi: apple: Add interrupt functionality
  2026-08-05 10:11 [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support Sasha Finkelstein
                   ` (4 preceding siblings ...)
  2026-08-05 10:11 ` [PATCH v4 5/6] spmi: apple: lock around FIFOs Sasha Finkelstein
@ 2026-08-05 10:11 ` Sasha Finkelstein
  2026-08-05 10:22   ` sashiko-bot
  2026-08-05 12:42   ` Janne Grunau
  5 siblings, 2 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 10:11 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: asahi, linux-arm-kernel, linux-kernel, devicetree,
	Sasha Finkelstein, Alba Mendez

From: Alba Mendez <me@alba.sh>

Add support for interrupts sent by slave devices and use IRQ for
RX FIFO if possible, as that 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@alba.sh>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
---
 drivers/spmi/Kconfig                 |   3 +-
 drivers/spmi/spmi-apple-controller.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 255 insertions(+), 2 deletions(-)

diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
index a80cf4047b86..7243863a09b4 100644
--- a/drivers/spmi/Kconfig
+++ b/drivers/spmi/Kconfig
@@ -13,7 +13,8 @@ if SPMI
 
 config SPMI_APPLE
 	tristate "Apple SoC SPMI Controller platform driver"
-	depends on ARCH_APPLE || COMPILE_TEST
+	select IRQ_DOMAIN_HIERARCHY
+	depends on ARCH_APPLE || (COMPILE_TEST && 64BIT)
 	help
 	  If you say yes to this option, support will be included for the
 	  SPMI controller present on many Apple SoCs, including the
diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
index c3bc1f6d5741..7e86d7983fb4 100644
--- a/drivers/spmi/spmi-apple-controller.c
+++ b/drivers/spmi/spmi-apple-controller.c
@@ -13,11 +13,17 @@
 
 #include <linux/bitfield.h>
 #include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
+#include <linux/spinlock.h>
 #include <linux/spmi.h>
 
 /* SPMI Controller Registers */
@@ -26,6 +32,13 @@
 #define SPMI_RSP_REG 0x8
 #define SPMI_ACT_REG 0xa4
 
+#define SPMI_IRQ_MASK_BASE 0x20
+#define SPMI_IRQ_ACK_BASE 0x60
+#define SPMI_NUM_PERIPHERAL_IRQS 256
+#define SPMI_NUM_IRQS (SPMI_NUM_PERIPHERAL_IRQS + 32)
+
+#define SPMI_IRQ_NOTIFY 256
+
 /* SPMI_RSP_REG reply word */
 #define SPMI_REPLY_FRAME_PARITY_STATUS GENMASK(31, 16)
 #define SPMI_REPLY_ACK BIT(15)
@@ -41,6 +54,12 @@
 struct apple_spmi {
 	void __iomem *regs;
 	struct mutex fifo_lock;
+	struct completion fifo_rx;
+	struct irq_domain *irqd;
+	raw_spinlock_t irq_mask_lock;
+	DECLARE_BITMAP(irq_mask_cache, SPMI_NUM_PERIPHERAL_IRQS);
+	int irq;
+	bool notify_irq;
 	bool prev_fail;
 };
 
@@ -48,6 +67,56 @@ struct apple_spmi {
 	readl_poll_timeout((spmi)->regs + (reg), (val), (cond), \
 			   REG_POLL_INTERVAL_US, REG_POLL_TIMEOUT_US)
 
+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 void apple_spmi_irq_ack(struct irq_data *d)
+{
+	struct apple_spmi *spmi = irq_data_get_irq_chip_data(d);
+
+	apple_spmi_irq_ack_raw(spmi, d->hwirq);
+}
+
+static void apple_spmi_irq_mask(struct irq_data *d)
+{
+	struct apple_spmi *spmi = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&spmi->irq_mask_lock, flags);
+	apple_spmi_irq_mask_raw(spmi, d->hwirq);
+	clear_bit(d->hwirq, spmi->irq_mask_cache);
+	raw_spin_unlock_irqrestore(&spmi->irq_mask_lock, flags);
+}
+
+static void apple_spmi_irq_unmask(struct irq_data *d)
+{
+	struct apple_spmi *spmi = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&spmi->irq_mask_lock, flags);
+	set_bit(d->hwirq, spmi->irq_mask_cache);
+	apple_spmi_irq_unmask_raw(spmi, d->hwirq);
+	raw_spin_unlock_irqrestore(&spmi->irq_mask_lock, flags);
+}
+
 static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 param)
 {
 	return opc | sid << 8 | (u32)param << 16 | (1 << 15);
@@ -60,7 +129,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->notify_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) {
 		spmi->prev_fail = true;
 		dev_err(&ctrl->dev,
@@ -85,8 +166,10 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 
 	if (spmi->prev_fail) {
 		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
+		apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY);
 		spmi->prev_fail = false;
 	}
+	reinit_completion(&spmi->fifo_rx);
 
 	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
 
@@ -188,6 +271,164 @@ static int spmi_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
 	return -EINVAL;
 }
 
+static int apple_spmi_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	/* all interrupts have MSI semantics */
+	return type == IRQ_TYPE_EDGE_RISING ? 0 : -EINVAL;
+}
+
+static struct irq_chip apple_spmi_irq_chip = {
+	.name = "apple_spmi",
+	.irq_mask = apple_spmi_irq_mask,
+	.irq_unmask = apple_spmi_irq_unmask,
+	.irq_ack = apple_spmi_irq_ack,
+	.irq_set_type = apple_spmi_irq_set_type,
+	.flags = IRQCHIP_ONESHOT_SAFE,
+};
+
+static int apple_spmi_irq_domain_map(struct irq_domain *irqd,
+					unsigned int irq, irq_hw_number_t hw)
+{
+	irq_domain_set_info(irqd, irq, hw, &apple_spmi_irq_chip, irqd->host_data,
+				handle_edge_irq, NULL, NULL);
+	return 0;
+}
+
+static int apple_spmi_irq_domain_translate(struct irq_domain *irqd,
+					struct irq_fwspec *fwspec,
+					unsigned long *hwirq,
+					unsigned int *type)
+{
+	u32 *args = fwspec->param;
+
+	if (fwspec->param_count != 2)
+		return -EINVAL;
+
+	if (args[0] >= SPMI_NUM_PERIPHERAL_IRQS)
+		return -EINVAL;
+	*hwirq = args[0];
+	*type = args[1] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+static int apple_spmi_irq_domain_alloc(struct irq_domain *irqd, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	unsigned int type = IRQ_TYPE_NONE;
+	struct irq_fwspec *fwspec = arg;
+	irq_hw_number_t hwirq;
+	int i, ret;
+
+	ret = apple_spmi_irq_domain_translate(irqd, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	if (hwirq + nr_irqs > SPMI_NUM_PERIPHERAL_IRQS)
+		return -EINVAL;
+
+	for (i = 0; i < nr_irqs; i++) {
+		ret = apple_spmi_irq_domain_map(irqd, virq + i, hwirq + i);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void apple_spmi_irq_domain_free(struct irq_domain *irqd, unsigned int virq,
+				unsigned int nr_irqs)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		struct irq_data *d = irq_domain_get_irq_data(irqd, virq + i);
+
+		irq_set_handler(virq + i, NULL);
+		irq_domain_reset_irq_data(d);
+	}
+}
+
+static const struct irq_domain_ops apple_spmi_irq_domain_ops = {
+	.translate	= apple_spmi_irq_domain_translate,
+	.alloc		= apple_spmi_irq_domain_alloc,
+	.free		= apple_spmi_irq_domain_free,
+};
+
+static void apple_spmi_irq_handler(struct irq_desc *desc)
+{
+	struct apple_spmi *spmi = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	bool handled = false;
+	unsigned long val, offset, bit;
+
+	chained_irq_enter(chip, desc);
+	val = readl(spmi->regs + SPMI_IRQ_ACK_BASE + (SPMI_IRQ_NOTIFY / 32) * 4);
+	if (val & BIT(SPMI_IRQ_NOTIFY % 32)) {
+		apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY);
+		complete(&spmi->fifo_rx);
+		handled = true;
+	}
+
+	for (offset = 0; offset < SPMI_NUM_PERIPHERAL_IRQS / 8; offset += sizeof(val)) {
+		val = readq(spmi->regs + SPMI_IRQ_ACK_BASE + offset);
+		/**
+		 * because of other masters in the bus, we're going to get a multitude of
+		 * interrupts we're not interested in. irq_resolve_mapping isn't very
+		 * optimized for the nonexistent path, so instead we mask with (a locally
+		 * cached version of) the IRQ mask
+		 */
+		val &= spmi->irq_mask_cache[offset / sizeof(val)];
+		for_each_set_bit(bit, &val, 64) {
+			generic_handle_domain_irq(spmi->irqd, offset * 8 + bit);
+			handled = true;
+			val &= ~BIT(bit);
+		}
+	}
+	if (!handled)
+		handle_bad_irq(desc);
+	chained_irq_exit(chip, desc);
+}
+
+static void remove_chained_handler(void *data)
+{
+	unsigned int irq = (unsigned int)(uintptr_t)data;
+
+	irq_set_chained_handler_and_data(irq, NULL, NULL);
+}
+
+static int apple_spmi_init_irq(struct platform_device *pdev,
+			  struct apple_spmi *spmi, int irq)
+{
+	int ret;
+	struct irq_domain_info info = {
+		.fwnode		= pdev->dev.fwnode,
+		.hwirq_max	= ~0U,
+		.ops		= &apple_spmi_irq_domain_ops,
+		.host_data	= spmi,
+	};
+
+	raw_spin_lock_init(&spmi->irq_mask_lock);
+
+	for (size_t offset = 0; offset < SPMI_NUM_IRQS / 8; offset += 4) {
+		writel(0, spmi->regs + SPMI_IRQ_MASK_BASE + offset);
+		writel(U32_MAX, spmi->regs + SPMI_IRQ_ACK_BASE + offset);
+	}
+
+	spmi->irqd = devm_irq_domain_instantiate(&pdev->dev, &info);
+	if (IS_ERR(spmi->irqd))
+		return PTR_ERR(spmi->irqd);
+
+	spmi->notify_irq = true;
+	ret = devm_add_action(&pdev->dev, remove_chained_handler, (void *)(uintptr_t)spmi->irq);
+	if (ret)
+		return ret;
+
+	irq_set_chained_handler_and_data(spmi->irq, apple_spmi_irq_handler, spmi);
+	apple_spmi_irq_unmask_raw(spmi, SPMI_IRQ_NOTIFY);
+
+	return 0;
+}
+
 static int apple_spmi_probe(struct platform_device *pdev)
 {
 	struct apple_spmi *spmi;
@@ -200,6 +441,8 @@ 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);
+	platform_set_drvdata(pdev, spmi);
 
 	spmi->regs = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(spmi->regs))
@@ -211,6 +454,15 @@ static int apple_spmi_probe(struct platform_device *pdev)
 	ctrl->write_cmd = spmi_write_cmd;
 	ctrl->cmd = spmi_cmd;
 
+	spmi->irq = platform_get_irq_optional(pdev, 0);
+	if (spmi->irq < 0 && spmi->irq != -ENXIO)
+		return spmi->irq;
+	if (spmi->irq >= 0) {
+		ret = apple_spmi_init_irq(pdev, spmi, spmi->irq);
+		if (ret)
+			return ret;
+	}
+
 	ret = devm_spmi_controller_add(&pdev->dev, ctrl);
 	if (ret)
 		return dev_err_probe(&pdev->dev, ret,

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x
  2026-08-05 10:11 ` [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x Sasha Finkelstein
@ 2026-08-05 10:19   ` sashiko-bot
  2026-08-05 12:01   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-05 10:19 UTC (permalink / raw)
  To: Sasha Finkelstein; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Addition of the `apple,t8122-spmi` compatible string is not documented in the commit message.
--

--- Patch [1]: [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x ---
commit 3674bf5dbfe7c309c197e5a76b4e84e891ec9d7d
Author: Sasha Finkelstein <k@chaosmail.tech>

dt-bindings: spmi: apple,spmi: Add t603x

This commit adds device tree binding support for the Apple t603x SPMI
controllers, including optional interrupt support for controllers that are
not always-on.

> diff --git a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
> --- a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
> +++ b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
[ ... ]
> @@ -20,8 +20,11 @@ properties:
>        - items:
>            - enum:
>                - apple,t6020-spmi
> +              - apple,t6030-spmi
> +              - apple,t6031-spmi
>                - apple,t8012-spmi
>                - apple,t8015-spmi
> +              - apple,t8122-spmi

[Severity: Medium]
The commit message and subject specifically mention adding support for the
t603x series, but this also adds the apple,t8122-spmi compatible string.
Should the commit description be updated to reflect the addition of t8122,
or was this new compatible string intended for a separate patch?

>            - const: apple,t8103-spmi
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260805-t603x-spmi-v4-0-c15a12d9a7d1@chaosmail.tech?part=1

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 2/6] spmi: apple: Validate FIFO state
  2026-08-05 10:11 ` [PATCH v4 2/6] spmi: apple: Validate FIFO state Sasha Finkelstein
@ 2026-08-05 10:21   ` sashiko-bot
  2026-08-05 12:03   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-05 10:21 UTC (permalink / raw)
  To: Sasha Finkelstein; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] The FIFO flush command is written to the wrong register (`SPMI_RSP_REG` instead of `SPMI_ACT_REG`).
- [High] Missing `spmi->prev_fail = true` assignment when aborting due to an empty RX FIFO.

Pre-existing issues:
- [High] Bus transactions are not serialized, leading to potential race conditions on the FIFO and controller registers.
--

--- Patch [2]: [PATCH v4 2/6] spmi: apple: Validate FIFO state ---
commit 22dd698ab8c4900ee8f167f7c5d03df7bd152f3b
Author: Alba Mendez <me@alba.sh>

spmi: apple: Validate FIFO state

Check for data before reading the body of a reply, and check for
end of data afterwards.

> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index 376cf682c43e0..4d5356bf9bbf1 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c
[ ... ]
> @@ -67,6 +71,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,

[Severity: High]
This is a pre-existing issue, but since spmi_read_cmd() lacks a mutex or 
spinlock, what prevents concurrent calls from different client drivers 
(like regulators or RTC) from interleaving?

Without serialization, concurrent accesses to the controller registers could 
corrupt the state or wedge the controller. This pre-existing issue also 
affects the newly added prev_fail flag, which could be overwritten or 
misread during interleaved bus operations.

>  	u8 i;
>  	int ret;
>  
> +	if (spmi->prev_fail) {
> +		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);

[Severity: High]
Is it intended to write SPMI_ACT_FIFO_FLUSH to SPMI_RSP_REG? 

It looks like this might have meant to write to the newly defined 
SPMI_ACT_REG instead. Writing an action command to the response register 
could leave the FIFO unflushed and leave the controller permanently wedged 
after an error.

> +		spmi->prev_fail = false;
> +	}
> +
>  	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
>  
>  	ret = apple_spmi_wait_rx_not_empty(ctrl);
[ ... ]
> @@ -78,6 +87,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  
>  	/* Read SPMI data reply */
>  	while (len_read < len) {
> +		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
> +			dev_err_ratelimited(&ctrl->dev,
> +					    "FIFO lacks reply data, controller stuck?\n");
> +			return -EIO;

[Severity: High]
When the loop aborts here due to an empty RX FIFO, shouldn't spmi->prev_fail 
be set to true before returning?

If the hardware response is slow and causes an early exit without setting 
this flag, the next transaction will not flush the delayed reply. Could this 
cause the subsequent transaction to read stale reply data as its own, leading 
to silent data corruption for downstream PMIC clients?

> +		}
>  		rsp = readl(spmi->regs + SPMI_RSP_REG);
>  		i = 0;
>  		while ((len_read < len) && (i < 4)) {
[ ... ]
> @@ -97,6 +116,11 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,

[Severity: High]
This is a pre-existing issue, but similar to spmi_read_cmd(), the 
spmi_write_cmd() function also lacks synchronization, leaving it vulnerable to 
concurrent accesses from different SPMI bus clients.

>  	size_t i = 0, j;
>  	int ret;
>  
> +	if (spmi->prev_fail) {
> +		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);

[Severity: High]
Does this also need to be written to SPMI_ACT_REG instead of SPMI_RSP_REG?

> +		spmi->prev_fail = false;
> +	}
> +

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260805-t603x-spmi-v4-0-c15a12d9a7d1@chaosmail.tech?part=2

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 6/6] spmi: apple: Add interrupt functionality
  2026-08-05 10:11 ` [PATCH v4 6/6] spmi: apple: Add interrupt functionality Sasha Finkelstein
@ 2026-08-05 10:22   ` sashiko-bot
  2026-08-05 12:42   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-05 10:22 UTC (permalink / raw)
  To: Sasha Finkelstein; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] Missing hardware mask and synchronization in the driver unbind path causes an interrupt storm and Use-After-Free.
- [High] A time-of-check to time-of-use (TOCTOU) race condition exists between the timeout error recovery path and the interrupt handler, causing spurious failures.

Pre-existing issues:
- [High] The error recovery logic writes the flush command to the wrong hardware register, rendering the FIFO flush ineffective.
--

--- Patch [6]: [PATCH v4 6/6] spmi: apple: Add interrupt functionality ---
commit e606557ef28a4c4b211e2c324dc5cf458fc57741
Author: Alba Mendez <me@alba.sh>

spmi: apple: Add interrupt functionality

Add support for interrupts sent by slave devices and use IRQ for
RX FIFO if possible, as that IRQ fires as soon as the reply is
available, which is usually takes a few us instead of the 10ms sleep
interval for polling

> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index c3bc1f6d57417..7e86d7983fb48 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c

[ ... ]

> @@ -85,8 +166,10 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  
>  	if (spmi->prev_fail) {
>  		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);

[Severity: High]
This is a pre-existing issue, but is this writing the flush command to the
wrong hardware register? 

The code writes SPMI_ACT_FIFO_FLUSH to SPMI_RSP_REG instead of SPMI_ACT_REG.
If a command times out, wouldn't this fail to flush the RX FIFO and cause
subsequent commands to incorrectly read the delayed response?

> +		apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY);
>  		spmi->prev_fail = false;
>  	}
> +	reinit_completion(&spmi->fifo_rx);

[Severity: High]
Is there a race condition here between the timeout error recovery path and
the interrupt handler? 

If a delayed response arrives just as we call reinit_completion() for the
next command:

apple_spmi_irq_handler() {
    ...
    if (val & BIT(SPMI_IRQ_NOTIFY % 32)) {
        apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY);
        complete(&spmi->fifo_rx);
    ...
}

Could the interrupt handler call complete() concurrently, causing the new
command to be falsely signaled as complete and potentially reading corrupted
data from the previous timed-out command?

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

[ ... ]

> @@ -188,6 +271,164 @@ static int spmi_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)

[ ... ]

> +static void remove_chained_handler(void *data)
> +{
> +	unsigned int irq = (unsigned int)(uintptr_t)data;
> +
> +	irq_set_chained_handler_and_data(irq, NULL, NULL);

[Severity: High]
Does this unbind path need to mask the hardware interrupt and synchronize
with in-flight handlers? 

When the driver is unbound, clearing the handler without masking
SPMI_IRQ_NOTIFY in the hardware or calling synchronize_irq() could leave
the interrupt firing concurrently. 

Could this cause an interrupt storm or a use-after-free when the handler
accesses unmapped device memory?

> +}

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260805-t603x-spmi-v4-0-c15a12d9a7d1@chaosmail.tech?part=6

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 4/6] spmi: apple: Implement remaining commands
  2026-08-05 10:11 ` [PATCH v4 4/6] spmi: apple: Implement remaining commands Sasha Finkelstein
@ 2026-08-05 10:24   ` sashiko-bot
  2026-08-05 12:19   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-05 10:24 UTC (permalink / raw)
  To: Sasha Finkelstein; +Cc: devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] Endianness portability bug causing data corruption on big-endian architectures due to `memcpy` and `writel()` interaction.

Pre-existing issues:
- [High] Missing lock around SPMI bus transactions allows concurrent operations to interleave, corrupting hardware state.
--

--- Patch [4]: [PATCH v4 4/6] spmi: apple: Implement remaining commands ---
commit 3757bcbae2fce4b6f6a6ec4bc0492ce9325dcd8c
Author: Alba Mendez <me@alba.sh>

spmi: apple: Implement remaining commands

Add support for zero write and power management commands

> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index 7ebf899edf173..49827e51e93e9 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c
> @@ -69,14 +69,14 @@ static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl)
>  	return 0;
>  }
>  
> -static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
> -			 u16 saddr, u8 *buf, size_t len)
> +static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
> +			 u16 param, const u8 *buf, size_t len, u8 *ibuf, size_t ilen)

[Severity: High]
This is a pre-existing issue, but is there a missing lock around the SPMI
bus transactions here?

The SPMI subsystem core does not serialize calls to controller read/write
functions. Without a mutex protecting this transaction sequence, concurrent
requests can interleave their writes to the shared command register,
poll operations, and reads from the response FIFO.

Additionally, the unprotected shared flag spmi->prev_fail introduces a race
condition where one thread's failure can cause another thread to
unexpectedly flush the controller's FIFO mid-transaction.

>  {
>  	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
> -	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
> +	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, param);
>  	u32 reply, rsp;
>  	size_t len_read = 0;
> -	u8 i;
> +	size_t i = 0, j;
>  	int ret;
>  
>  	if (spmi->prev_fail) {

[ ... ]

> @@ -86,6 +86,14 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  
>  	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
>  
> +	while (i < len) {
> +		j = min_t(size_t, sizeof(spmi_cmd), len - i);
> +		spmi_cmd = 0;
> +		memcpy(&spmi_cmd, buf + i, j);
> +		writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);

[Severity: High]
Will this byte stream copy cause an endianness bug on big-endian
architectures?

Using memcpy to copy the byte stream into a local u32 spmi_cmd
variable and then calling writel() will result in byteswapping on
big-endian systems.

The memcpy call places the first byte of buf into the most significant
byte of the u32, and writel() performs a CPU-to-LE conversion.

This reverses the byte order sent to the hardware compared to the previous
bitwise-shift implementation, which was correctly endian-safe. Should this
use writesl() or continue using the previous shift-based packing?

> +		i += j;
> +	}
> +
>  	ret = apple_spmi_wait_rx_not_empty(ctrl);
>  	if (ret)
>  		return ret;
> @@ -93,7 +101,7 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	reply = readl(spmi->regs + SPMI_RSP_REG);
>  
>  	/* Read SPMI data reply */
> -	while (len_read < len) {
> +	while (len_read < ilen) {
>  		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
>  			spmi->prev_fail = true;
>  			dev_err_ratelimited(&ctrl->dev,
> @@ -101,11 +109,9 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  			return -EIO;
>  		}
>  		rsp = readl(spmi->regs + SPMI_RSP_REG);
> -		i = 0;
> -		while ((len_read < len) && (i < 4)) {
> -			buf[len_read++] = ((0xff << (8 * i)) & rsp) >> (8 * i);
> -			i += 1;
> -		}
> +		i = min_t(size_t, sizeof(spmi_cmd), ilen - len_read);
> +		memcpy(ibuf + len_read, &rsp, i);

[Severity: High]
Does unpacking the readl() result via memcpy introduce the same
endianness reversal on big-endian systems?

Since rsp is read via readl(), copying it directly to the buffer with
memcpy will reverse the byte order.

> +		len_read += i;
>  	}
>  
>  	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260805-t603x-spmi-v4-0-c15a12d9a7d1@chaosmail.tech?part=4

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 5/6] spmi: apple: lock around FIFOs
  2026-08-05 10:11 ` [PATCH v4 5/6] spmi: apple: lock around FIFOs Sasha Finkelstein
@ 2026-08-05 10:25   ` sashiko-bot
  2026-08-05 12:25   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-05 10:25 UTC (permalink / raw)
  To: Sasha Finkelstein; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Copying raw bytes to/from u32 variables for writel/readl causes byte swapping and data corruption on big-endian architectures.
- [High] The FIFO flush command is written to the response register (SPMI_RSP_REG) instead of the action register (SPMI_ACT_REG).
--

--- Patch [5]: [PATCH v4 5/6] spmi: apple: lock around FIFOs ---
commit 69b70a7086a8bbc17681a468c0f31368df078c1d
Author: Alba Mendez <me@alba.sh>

spmi: apple: lock around FIFOs

The driver was missing locking around register interactions.

> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c

[ ... ]

> @@ -79,6 +81,8 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	size_t i = 0, j;
>  	int ret;
>  
> +	guard(mutex)(&spmi->fifo_lock);
> +
>  	if (spmi->prev_fail) {
>  		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);

[Severity: High]
Does this flush command write to the correct register?

Looking at the error recovery path in spmi_raw_cmd(), the FIFO flush is
written to the response register:

    writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);

Should this be written to SPMI_ACT_REG instead? Writing the flush command
to the response register might fail to flush the hardware FIFO and could
corrupt the response register state.

[Severity: High]
Can this I/O access pattern corrupt data on big-endian architectures?

Further down in spmi_raw_cmd(), the command and response byte streams are
accessed using writel() and readl():

    memcpy(&spmi_cmd, buf + i, j);
    writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);

and during the read phase:

    rsp = readl(spmi->regs + SPMI_RSP_REG);
    i = min_t(size_t, sizeof(spmi_cmd), ilen - len_read);
    memcpy(ibuf + len_read, &rsp, i);

Copying a byte stream into a local u32 variable and then using writel() or
readl() performs CPU-to-device endianness conversion, which will swap the
bytes on big-endian systems.

Should these accesses use stream-based accessors like writesl() and readsl()
instead to preserve the expected byte order?

>  		spmi->prev_fail = false;
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260805-t603x-spmi-v4-0-c15a12d9a7d1@chaosmail.tech?part=5

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 3/6] spmi: apple: check transaction status
  2026-08-05 10:11 ` [PATCH v4 3/6] spmi: apple: check transaction status Sasha Finkelstein
@ 2026-08-05 10:27   ` sashiko-bot
  2026-08-05 12:06   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-08-05 10:27 UTC (permalink / raw)
  To: Sasha Finkelstein; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Missing synchronization in SPMI controller read/write callbacks allows concurrent bus transactions to corrupt controller registers and shared state.
- [High] The error recovery logic writes the FIFO flush command to the read-only response register instead of the action register.
- [High] Multiple signed integer overflows due to unsafe bitwise shifts result in undefined behavior.
--

--- Patch [3]: [PATCH v4 3/6] spmi: apple: check transaction status ---
commit 879bd117b98da2cbe05de1381fdc2003dc4e22ae
Author: Alba Mendez <me@alba.sh>

spmi: apple: check transaction status

This patch updates the Apple SPMI controller driver to check for parity errors
and missing command acknowledgments during transactions. By verifying the parity
status and ACK bits in the reply frame, the driver can now detect and report
transaction failures instead of silently ignoring them.

Signed-off-by: Alba Mendez <me@alba.sh>
Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>

> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index 4d5356bf9bbf1..7ebf899edf173 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c

[ ... ]

> @@ -66,7 +74,7 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  {
>  	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);

[Severity: High]
This isn't a bug introduced by this patch, but does the lack of a lock here
allow concurrent bus transactions to corrupt controller registers and shared
state?

The SPMI framework does not serialize read_cmd and write_cmd controller
callbacks. Multiple PMIC device drivers on the same SPMI bus could initiate
transactions concurrently.

Without a spinlock or mutex, wouldn't concurrent accesses interleave writes
to SPMI_CMD_REG and cause race conditions when handling the shared
spmi->prev_fail state?

>  	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);

[Severity: High]
This is a pre-existing issue, but do multiple bitwise shifts in this driver
invoke undefined behavior via signed integer overflow?

In apple_spmi_pack_cmd(), when packing the command:

return opc | sid << 8 | saddr << 16 | (len - 1) | (1 << 15);

If saddr is >= 0x8000, it gets promoted to a signed int and shifted by 16,
which overflows into the sign bit.

Similarly, in spmi_write_cmd() where buf[i++] is shifted by 24 (when >= 128),
and in spmi_read_cmd() where 0xff is shifted by 24, these shift signed ints
into the sign bit.

Could this allow the compiler to miscompile the addressing and data unpacking
logic?

> -	u32 rsp;
> +	u32 reply, rsp;
>  	size_t len_read = 0;
>  	u8 i;

[ ... ]

> @@ -82,12 +90,12 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	if (ret)
>  		return ret;
>  
> -	/* Discard SPMI reply status */
> -	readl(spmi->regs + SPMI_RSP_REG);
> +	reply = readl(spmi->regs + SPMI_RSP_REG);
>  
>  	/* Read SPMI data reply */
>  	while (len_read < len) {
>  		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
> +			spmi->prev_fail = true;

[Severity: High]
This isn't a bug introduced by this patch, but since we are actively setting
spmi->prev_fail to true here, does the error recovery logic write the FIFO
flush command to the wrong register?

When recovering from a previous failure, the driver does:

writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);

SPMI_RSP_REG is a read-only response register. Shouldn't this flush command
be written to the action register (SPMI_ACT_REG) instead? Otherwise, the
FIFO might never be flushed, leading to a permanently stuck controller state.

>  			dev_err_ratelimited(&ctrl->dev,
>  					    "FIFO lacks reply data, controller stuck?\n");
>  			return -EIO;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260805-t603x-spmi-v4-0-c15a12d9a7d1@chaosmail.tech?part=3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x
  2026-08-05 10:11 ` [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x Sasha Finkelstein
  2026-08-05 10:19   ` sashiko-bot
@ 2026-08-05 12:01   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: Janne Grunau @ 2026-08-05 12:01 UTC (permalink / raw)
  To: Sasha Finkelstein
  Cc: Sven Peter, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, asahi, linux-arm-kernel,
	linux-kernel, devicetree

On Wed, Aug 05, 2026 at 12:11:11PM +0200, Sasha Finkelstein wrote:
> Add t603x compatibles, interrupt support, and support for SPMI
> controllers that are not always-on.
> 
> Keeping the "interrupt-controller" property as optional, as taking the
> j514c as an example, we only need this functionality on 3 out of 10
> SPMI controllers present on this SoC.
> 
> Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
> ---
>  Documentation/devicetree/bindings/spmi/apple,spmi.yaml | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
> index 3e5b14bc8c31..fceed47b2cd1 100644
> --- a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
> +++ b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml
> @@ -20,8 +20,11 @@ properties:
>        - items:
>            - enum:
>                - apple,t6020-spmi
> +              - apple,t6030-spmi
> +              - apple,t6031-spmi
>                - apple,t8012-spmi
>                - apple,t8015-spmi
> +              - apple,t8122-spmi
>            - const: apple,t8103-spmi
>        - items:
>            - enum:
> @@ -34,6 +37,18 @@ properties:
>    reg:
>      maxItems: 1
>  
> +  interrupts:
> +    maxItems: 1
> +    description: Optional, operates in polled mode if not present
> +
> +  interrupt-controller: true
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  power-domains:
> +    maxItems: 1
> +
>  required:
>    - compatible
>    - reg

Reviewed-by: Janne Grunau <j@jannau.net>

Janne

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 2/6] spmi: apple: Validate FIFO state
  2026-08-05 10:11 ` [PATCH v4 2/6] spmi: apple: Validate FIFO state Sasha Finkelstein
  2026-08-05 10:21   ` sashiko-bot
@ 2026-08-05 12:03   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: Janne Grunau @ 2026-08-05 12:03 UTC (permalink / raw)
  To: Sasha Finkelstein
  Cc: Sven Peter, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, asahi, linux-arm-kernel,
	linux-kernel, devicetree, Alba Mendez

On Wed, Aug 05, 2026 at 12:11:12PM +0200, Sasha Finkelstein wrote:
> From: Alba Mendez <me@alba.sh>
> 
> Check for data before reading the body of a reply, and check for
> end of data afterwards.
> 
> Signed-off-by: Alba Mendez <me@alba.sh>
> Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
> ---
>  drivers/spmi/spmi-apple-controller.c | 29 +++++++++++++++++++++++++++++
>  1 file changed, 29 insertions(+)
> 
> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index 376cf682c43e..4d5356bf9bbf 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c
> @@ -21,7 +21,9 @@
>  #define SPMI_STATUS_REG 0
>  #define SPMI_CMD_REG 0x4
>  #define SPMI_RSP_REG 0x8
> +#define SPMI_ACT_REG 0xa4
>  
> +#define SPMI_ACT_FIFO_FLUSH BIT(0)
>  #define SPMI_RX_FIFO_EMPTY BIT(24)
>  
>  #define REG_POLL_INTERVAL_US 10000
> @@ -29,6 +31,7 @@
>  
>  struct apple_spmi {
>  	void __iomem *regs;
> +	bool prev_fail;
>  };
>  
>  #define poll_reg(spmi, reg, val, cond) \
> @@ -49,6 +52,7 @@ static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl)
>  
>  	ret = poll_reg(spmi, SPMI_STATUS_REG, status, !(status & SPMI_RX_FIFO_EMPTY));
>  	if (ret) {
> +		spmi->prev_fail = true;
>  		dev_err(&ctrl->dev,
>  			"failed to wait for RX FIFO not empty\n");
>  		return ret;
> @@ -67,6 +71,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	u8 i;
>  	int ret;
>  
> +	if (spmi->prev_fail) {
> +		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
> +		spmi->prev_fail = false;
> +	}
> +
>  	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
>  
>  	ret = apple_spmi_wait_rx_not_empty(ctrl);
> @@ -78,6 +87,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  
>  	/* Read SPMI data reply */
>  	while (len_read < len) {
> +		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
> +			dev_err_ratelimited(&ctrl->dev,
> +					    "FIFO lacks reply data, controller stuck?\n");
> +			return -EIO;
> +		}
>  		rsp = readl(spmi->regs + SPMI_RSP_REG);
>  		i = 0;
>  		while ((len_read < len) && (i < 4)) {
> @@ -86,6 +100,11 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  		}
>  	}
>  
> +	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
> +		dev_warn(&ctrl->dev, "FIFO has extra data\n");
> +		spmi->prev_fail = true;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -97,6 +116,11 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	size_t i = 0, j;
>  	int ret;
>  
> +	if (spmi->prev_fail) {
> +		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
> +		spmi->prev_fail = false;
> +	}
> +
>  	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
>  
>  	while (i < len) {
> @@ -115,6 +139,11 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	/* Discard */
>  	readl(spmi->regs + SPMI_RSP_REG);
>  
> +	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
> +		dev_warn(&ctrl->dev, "FIFO has extra data\n");
> +		spmi->prev_fail = true;
> +	}
> +
>  	return 0;
>  }

Reviewed-by: Janne Grunau <j@jannau.net>

Janne

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 3/6] spmi: apple: check transaction status
  2026-08-05 10:11 ` [PATCH v4 3/6] spmi: apple: check transaction status Sasha Finkelstein
  2026-08-05 10:27   ` sashiko-bot
@ 2026-08-05 12:06   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: Janne Grunau @ 2026-08-05 12:06 UTC (permalink / raw)
  To: Sasha Finkelstein
  Cc: Sven Peter, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, asahi, linux-arm-kernel,
	linux-kernel, devicetree, Alba Mendez

On Wed, Aug 05, 2026 at 12:11:13PM +0200, Sasha Finkelstein wrote:
> From: Alba Mendez <me@alba.sh>
> 
> Check for parity errors and missing command ACKs
> 
> Signed-off-by: Alba Mendez <me@alba.sh>
> Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
> ---
>  drivers/spmi/spmi-apple-controller.c | 26 +++++++++++++++++++++-----
>  1 file changed, 21 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index 4d5356bf9bbf..7ebf899edf17 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/bitfield.h>
> +#include <linux/bits.h>
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
>  #include <linux/module.h>
> @@ -23,6 +25,12 @@
>  #define SPMI_RSP_REG 0x8
>  #define SPMI_ACT_REG 0xa4
>  
> +/* SPMI_RSP_REG reply word */
> +#define SPMI_REPLY_FRAME_PARITY_STATUS GENMASK(31, 16)
> +#define SPMI_REPLY_ACK BIT(15)
> +#define SPMI_REPLY_SLAVE_ID GENMASK(14, 8)
> +#define SPMI_REPLY_CMD GENMASK(7, 0)
> +
>  #define SPMI_ACT_FIFO_FLUSH BIT(0)
>  #define SPMI_RX_FIFO_EMPTY BIT(24)
>  
> @@ -66,7 +74,7 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  {
>  	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
>  	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
> -	u32 rsp;
> +	u32 reply, rsp;
>  	size_t len_read = 0;
>  	u8 i;
>  	int ret;
> @@ -82,12 +90,12 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	if (ret)
>  		return ret;
>  
> -	/* Discard SPMI reply status */
> -	readl(spmi->regs + SPMI_RSP_REG);
> +	reply = readl(spmi->regs + SPMI_RSP_REG);
>  
>  	/* Read SPMI data reply */
>  	while (len_read < len) {
>  		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
> +			spmi->prev_fail = true;

should this have been in the previous patch? Not sure if the flush helps
in this case but the line looks in this change out-of-place

>  			dev_err_ratelimited(&ctrl->dev,
>  					    "FIFO lacks reply data, controller stuck?\n");
>  			return -EIO;
> @@ -105,6 +113,10 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  		spmi->prev_fail = true;
>  	}
>  
> +	if (~FIELD_GET(SPMI_REPLY_FRAME_PARITY_STATUS, reply) & ((1 << len) - 1)) {
> +		dev_err(&ctrl->dev, "some frames failed parity check\n");
> +		return -EIO;
> +	}
>  	return 0;
>  }
>  
> @@ -113,6 +125,7 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  {
>  	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
>  	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
> +	u32 reply;
>  	size_t i = 0, j;
>  	int ret;
>  
> @@ -136,14 +149,17 @@ static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	if (ret)
>  		return ret;
>  
> -	/* Discard */
> -	readl(spmi->regs + SPMI_RSP_REG);
> +	reply = readl(spmi->regs + SPMI_RSP_REG);
>  
>  	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
>  		dev_warn(&ctrl->dev, "FIFO has extra data\n");
>  		spmi->prev_fail = true;
>  	}
>  
> +	if (!FIELD_GET(SPMI_REPLY_ACK, reply)) {
> +		dev_err(&ctrl->dev, "command not acknowledged\n");
> +		return -EIO;
> +	}
>  	return 0;
>  }

besides the single misplaced line

Reviewed-by: Janne Grunau <j@jannau.net>

Janne

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 4/6] spmi: apple: Implement remaining commands
  2026-08-05 10:11 ` [PATCH v4 4/6] spmi: apple: Implement remaining commands Sasha Finkelstein
  2026-08-05 10:24   ` sashiko-bot
@ 2026-08-05 12:19   ` Janne Grunau
  2026-08-05 16:54     ` Sasha Finkelstein
  1 sibling, 1 reply; 20+ messages in thread
From: Janne Grunau @ 2026-08-05 12:19 UTC (permalink / raw)
  To: Sasha Finkelstein
  Cc: Sven Peter, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, asahi, linux-arm-kernel,
	linux-kernel, devicetree, Alba Mendez

On Wed, Aug 05, 2026 at 12:11:14PM +0200, Sasha Finkelstein wrote:
> From: Alba Mendez <me@alba.sh>
> 
> Add support for zero write and power management commands

missing dot at the end of the sentence.

> Signed-off-by: Alba Mendez <me@alba.sh>
> Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
> ---
>  drivers/spmi/spmi-apple-controller.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------
>  1 file changed, 68 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index 7ebf899edf17..49827e51e93e 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c
> @@ -46,9 +46,9 @@ struct apple_spmi {
>  	readl_poll_timeout((spmi)->regs + (reg), (val), (cond), \
>  			   REG_POLL_INTERVAL_US, REG_POLL_TIMEOUT_US)
>  
> -static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 saddr, size_t len)
> +static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 param)
>  {
> -	return opc | sid << 8 | saddr << 16 | (len - 1) | (1 << 15);
> +	return opc | sid << 8 | (u32)param << 16 | (1 << 15);
>  }
>  
>  /* Wait for Rx FIFO to have something */
> @@ -69,14 +69,14 @@ static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl)
>  	return 0;
>  }
>  
> -static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
> -			 u16 saddr, u8 *buf, size_t len)
> +static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
> +			 u16 param, const u8 *buf, size_t len, u8 *ibuf, size_t ilen)

Instead of two sets of not very distinctive variable names ("buf"/"len",
"ibuf"/ilen") please use just buf/len and a boolean to indicate whether
the command is a read or write if you insists of routing all commands
through a single function. This can be simply added as first condition
in the "while" iterating over the write/read buffers.

>  {
>  	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
> -	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
> +	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, param);
>  	u32 reply, rsp;
>  	size_t len_read = 0;
> -	u8 i;
> +	size_t i = 0, j;
>  	int ret;
>  
>  	if (spmi->prev_fail) {
> @@ -86,6 +86,14 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  
>  	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
>  
> +	while (i < len) {
> +		j = min_t(size_t, sizeof(spmi_cmd), len - i);
> +		spmi_cmd = 0;
> +		memcpy(&spmi_cmd, buf + i, j);
> +		writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
> +		i += j;
> +	}
> +
>  	ret = apple_spmi_wait_rx_not_empty(ctrl);
>  	if (ret)
>  		return ret;
> @@ -93,7 +101,7 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	reply = readl(spmi->regs + SPMI_RSP_REG);
>  
>  	/* Read SPMI data reply */
> -	while (len_read < len) {
> +	while (len_read < ilen) {
>  		if (readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY) {
>  			spmi->prev_fail = true;
>  			dev_err_ratelimited(&ctrl->dev,
> @@ -101,11 +109,9 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  			return -EIO;
>  		}
>  		rsp = readl(spmi->regs + SPMI_RSP_REG);
> -		i = 0;
> -		while ((len_read < len) && (i < 4)) {
> -			buf[len_read++] = ((0xff << (8 * i)) & rsp) >> (8 * i);
> -			i += 1;
> -		}
> +		i = min_t(size_t, sizeof(spmi_cmd), ilen - len_read);
> +		memcpy(ibuf + len_read, &rsp, i);
> +		len_read += i;
>  	}
>  
>  	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
> @@ -113,54 +119,69 @@ static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  		spmi->prev_fail = true;
>  	}
>  
> -	if (~FIELD_GET(SPMI_REPLY_FRAME_PARITY_STATUS, reply) & ((1 << len) - 1)) {
> +	if (!ilen && !FIELD_GET(SPMI_REPLY_ACK, reply)) {
> +		dev_err(&ctrl->dev, "command not acknowledged\n");
> +		return -EIO;
> +	}
> +	if (~FIELD_GET(SPMI_REPLY_FRAME_PARITY_STATUS, reply) & ((1 << ilen) - 1)) {
>  		dev_err(&ctrl->dev, "some frames failed parity check\n");
>  		return -EIO;
>  	}
>  	return 0;
>  }
>  
> -static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
> -			  u16 saddr, const u8 *buf, size_t len)
> +/* Send a raw command with 1..16 input data frames */
> +static int spmi_raw_cmd_input(struct spmi_controller *ctrl, u8 opc, u8 sid,
> +			 u16 param, u8 *buf, size_t len)
>  {
> -	struct apple_spmi *spmi = spmi_controller_get_drvdata(ctrl);
> -	u32 spmi_cmd = apple_spmi_pack_cmd(opc, sid, saddr, len);
> -	u32 reply;
> -	size_t i = 0, j;
> -	int ret;
> -
> -	if (spmi->prev_fail) {
> -		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
> -		spmi->prev_fail = false;
> -	}
> -
> -	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
> +	return spmi_raw_cmd(ctrl, opc, sid, param, NULL, 0, buf, len);
> +}
>  
> -	while (i < len) {
> -		j = 0;
> -		spmi_cmd = 0;
> -		while ((j < 4) & (i < len))
> -			spmi_cmd |= buf[i++] << (j++ * 8);
> +/* Send a raw command with (optional) body and an input ACK */
> +static int spmi_raw_cmd_ack(struct spmi_controller *ctrl, u8 opc, u8 sid,
> +			  u16 param, const u8 *buf, size_t len)
> +{
> +	return spmi_raw_cmd(ctrl, opc, sid, param, buf, len, NULL, 0);
> +}
>  
> -		writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
> +static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
> +			 u16 saddr, u8 *buf, size_t len)
> +{
> +	switch (opc) {
> +	case SPMI_CMD_EXT_READ:
> +	case SPMI_CMD_EXT_READL:
> +		return spmi_raw_cmd_input(ctrl, opc | (len - 1), sid, saddr, buf, len);
> +	case SPMI_CMD_READ:
> +		return spmi_raw_cmd_input(ctrl, opc | saddr, sid, saddr, buf, len);
>  	}
> +	return -EINVAL;
> +}
>  
> -	ret = apple_spmi_wait_rx_not_empty(ctrl);
> -	if (ret)
> -		return ret;
> -
> -	reply = readl(spmi->regs + SPMI_RSP_REG);
> -
> -	if (!(readl(spmi->regs + SPMI_STATUS_REG) & SPMI_RX_FIFO_EMPTY)) {
> -		dev_warn(&ctrl->dev, "FIFO has extra data\n");
> -		spmi->prev_fail = true;
> +static int spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
> +			  u16 saddr, const u8 *buf, size_t len)
> +{
> +	switch (opc) {
> +	case SPMI_CMD_WRITE:
> +		return spmi_raw_cmd_ack(ctrl, opc | saddr, sid, buf[0] << 8 | saddr, NULL, 0);
> +	case SPMI_CMD_ZERO_WRITE:
> +		return spmi_raw_cmd_ack(ctrl, opc | buf[0], sid, buf[0] << 8 | saddr, NULL, 0);
> +	case SPMI_CMD_EXT_WRITE:
> +	case SPMI_CMD_EXT_WRITEL:
> +		return spmi_raw_cmd_ack(ctrl, opc | (len - 1), sid, saddr, buf, len);
>  	}
> +	return -EINVAL;
> +}
>  
> -	if (!FIELD_GET(SPMI_REPLY_ACK, reply)) {
> -		dev_err(&ctrl->dev, "command not acknowledged\n");
> -		return -EIO;
> +static int spmi_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
> +{
> +	switch (opc) {
> +	case SPMI_CMD_RESET:
> +	case SPMI_CMD_SLEEP:
> +	case SPMI_CMD_SHUTDOWN:
> +	case SPMI_CMD_WAKEUP:
> +		return spmi_raw_cmd_ack(ctrl, opc, sid, 0, NULL, 0);
>  	}
> -	return 0;
> +	return -EINVAL;
>  }
>  
>  static int apple_spmi_probe(struct platform_device *pdev)
> @@ -183,6 +204,7 @@ static int apple_spmi_probe(struct platform_device *pdev)
>  
>  	ctrl->read_cmd = spmi_read_cmd;
>  	ctrl->write_cmd = spmi_write_cmd;
> +	ctrl->cmd = spmi_cmd;
>  
>  	ret = devm_spmi_controller_add(&pdev->dev, ctrl);
>  	if (ret)

looks good otherwise

Janne

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 5/6] spmi: apple: lock around FIFOs
  2026-08-05 10:11 ` [PATCH v4 5/6] spmi: apple: lock around FIFOs Sasha Finkelstein
  2026-08-05 10:25   ` sashiko-bot
@ 2026-08-05 12:25   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: Janne Grunau @ 2026-08-05 12:25 UTC (permalink / raw)
  To: Sasha Finkelstein
  Cc: Sven Peter, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, asahi, linux-arm-kernel,
	linux-kernel, devicetree, Alba Mendez

On Wed, Aug 05, 2026 at 12:11:15PM +0200, Sasha Finkelstein wrote:
> From: Alba Mendez <me@alba.sh>
> 
> The driver was missing locking around register interactions

Missing '.' a the end of the sentence.
 
> Signed-off-by: Alba Mendez <me@alba.sh>
> Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
> ---
>  drivers/spmi/spmi-apple-controller.c | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index 49827e51e93e..c3bc1f6d5741 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c
> @@ -16,6 +16,7 @@
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
>  #include <linux/module.h>
> +#include <linux/mutex.h>
>  #include <linux/platform_device.h>
>  #include <linux/spmi.h>
>  
> @@ -39,6 +40,7 @@
>  
>  struct apple_spmi {
>  	void __iomem *regs;
> +	struct mutex fifo_lock;
>  	bool prev_fail;
>  };
>  
> @@ -79,6 +81,8 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  	size_t i = 0, j;
>  	int ret;
>  
> +	guard(mutex)(&spmi->fifo_lock);
> +
>  	if (spmi->prev_fail) {
>  		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
>  		spmi->prev_fail = false;
> @@ -195,6 +199,7 @@ static int apple_spmi_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  
>  	spmi = spmi_controller_get_drvdata(ctrl);
> +	mutex_init(&spmi->fifo_lock);
>  
>  	spmi->regs = devm_platform_ioremap_resource(pdev, 0);
>  	if (IS_ERR(spmi->regs))

Reviewed-by: Janne Grunau <j@jannau.net>

Janne

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 6/6] spmi: apple: Add interrupt functionality
  2026-08-05 10:11 ` [PATCH v4 6/6] spmi: apple: Add interrupt functionality Sasha Finkelstein
  2026-08-05 10:22   ` sashiko-bot
@ 2026-08-05 12:42   ` Janne Grunau
  1 sibling, 0 replies; 20+ messages in thread
From: Janne Grunau @ 2026-08-05 12:42 UTC (permalink / raw)
  To: Sasha Finkelstein
  Cc: Sven Peter, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, asahi, linux-arm-kernel,
	linux-kernel, devicetree, Alba Mendez

On Wed, Aug 05, 2026 at 12:11:16PM +0200, Sasha Finkelstein wrote:
> From: Alba Mendez <me@alba.sh>
> 
> Add support for interrupts sent by slave devices and use IRQ for
> RX FIFO if possible, as that IRQ fires as soon as the reply is
> available, which is usually takes a few us instead of the 10ms sleep
> interval for polling

same as for the other changes, better check all of them because I
probably missed that before.

> Signed-off-by: Alba Mendez <me@alba.sh>
> Signed-off-by: Sasha Finkelstein <k@chaosmail.tech>
> ---
>  drivers/spmi/Kconfig                 |   3 +-
>  drivers/spmi/spmi-apple-controller.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 255 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
> index a80cf4047b86..7243863a09b4 100644
> --- a/drivers/spmi/Kconfig
> +++ b/drivers/spmi/Kconfig
> @@ -13,7 +13,8 @@ if SPMI
>  
>  config SPMI_APPLE
>  	tristate "Apple SoC SPMI Controller platform driver"
> -	depends on ARCH_APPLE || COMPILE_TEST
> +	select IRQ_DOMAIN_HIERARCHY
> +	depends on ARCH_APPLE || (COMPILE_TEST && 64BIT)
>  	help
>  	  If you say yes to this option, support will be included for the
>  	  SPMI controller present on many Apple SoCs, including the
> diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c
> index c3bc1f6d5741..7e86d7983fb4 100644
> --- a/drivers/spmi/spmi-apple-controller.c
> +++ b/drivers/spmi/spmi-apple-controller.c
> @@ -13,11 +13,17 @@
>  
>  #include <linux/bitfield.h>
>  #include <linux/bits.h>
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  #include <linux/platform_device.h>
> +#include <linux/spinlock.h>
>  #include <linux/spmi.h>
>  
>  /* SPMI Controller Registers */
> @@ -26,6 +32,13 @@
>  #define SPMI_RSP_REG 0x8
>  #define SPMI_ACT_REG 0xa4
>  
> +#define SPMI_IRQ_MASK_BASE 0x20
> +#define SPMI_IRQ_ACK_BASE 0x60
> +#define SPMI_NUM_PERIPHERAL_IRQS 256
> +#define SPMI_NUM_IRQS (SPMI_NUM_PERIPHERAL_IRQS + 32)
> +
> +#define SPMI_IRQ_NOTIFY 256
> +
>  /* SPMI_RSP_REG reply word */
>  #define SPMI_REPLY_FRAME_PARITY_STATUS GENMASK(31, 16)
>  #define SPMI_REPLY_ACK BIT(15)
> @@ -41,6 +54,12 @@
>  struct apple_spmi {
>  	void __iomem *regs;
>  	struct mutex fifo_lock;
> +	struct completion fifo_rx;
> +	struct irq_domain *irqd;
> +	raw_spinlock_t irq_mask_lock;
> +	DECLARE_BITMAP(irq_mask_cache, SPMI_NUM_PERIPHERAL_IRQS);
> +	int irq;
> +	bool notify_irq;
>  	bool prev_fail;
>  };
>  
> @@ -48,6 +67,56 @@ struct apple_spmi {
>  	readl_poll_timeout((spmi)->regs + (reg), (val), (cond), \
>  			   REG_POLL_INTERVAL_US, REG_POLL_TIMEOUT_US)
>  
> +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 void apple_spmi_irq_ack(struct irq_data *d)
> +{
> +	struct apple_spmi *spmi = irq_data_get_irq_chip_data(d);
> +
> +	apple_spmi_irq_ack_raw(spmi, d->hwirq);
> +}
> +
> +static void apple_spmi_irq_mask(struct irq_data *d)
> +{
> +	struct apple_spmi *spmi = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&spmi->irq_mask_lock, flags);
> +	apple_spmi_irq_mask_raw(spmi, d->hwirq);
> +	clear_bit(d->hwirq, spmi->irq_mask_cache);
> +	raw_spin_unlock_irqrestore(&spmi->irq_mask_lock, flags);
> +}
> +
> +static void apple_spmi_irq_unmask(struct irq_data *d)
> +{
> +	struct apple_spmi *spmi = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&spmi->irq_mask_lock, flags);
> +	set_bit(d->hwirq, spmi->irq_mask_cache);
> +	apple_spmi_irq_unmask_raw(spmi, d->hwirq);
> +	raw_spin_unlock_irqrestore(&spmi->irq_mask_lock, flags);
> +}
> +
>  static inline u32 apple_spmi_pack_cmd(u8 opc, u8 sid, u16 param)
>  {
>  	return opc | sid << 8 | (u32)param << 16 | (1 << 15);
> @@ -60,7 +129,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->notify_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) {
>  		spmi->prev_fail = true;
>  		dev_err(&ctrl->dev,
> @@ -85,8 +166,10 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>  
>  	if (spmi->prev_fail) {
>  		writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_RSP_REG);
> +		apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY);
>  		spmi->prev_fail = false;
>  	}
> +	reinit_completion(&spmi->fifo_rx);
>  
>  	writel(spmi_cmd, spmi->regs + SPMI_CMD_REG);
>  
> @@ -188,6 +271,164 @@ static int spmi_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
>  	return -EINVAL;
>  }
>  
> +static int apple_spmi_irq_set_type(struct irq_data *d, unsigned int type)
> +{
> +	/* all interrupts have MSI semantics */
> +	return type == IRQ_TYPE_EDGE_RISING ? 0 : -EINVAL;
> +}
> +
> +static struct irq_chip apple_spmi_irq_chip = {
> +	.name = "apple_spmi",
> +	.irq_mask = apple_spmi_irq_mask,
> +	.irq_unmask = apple_spmi_irq_unmask,
> +	.irq_ack = apple_spmi_irq_ack,
> +	.irq_set_type = apple_spmi_irq_set_type,
> +	.flags = IRQCHIP_ONESHOT_SAFE,
> +};
> +
> +static int apple_spmi_irq_domain_map(struct irq_domain *irqd,
> +					unsigned int irq, irq_hw_number_t hw)
> +{
> +	irq_domain_set_info(irqd, irq, hw, &apple_spmi_irq_chip, irqd->host_data,
> +				handle_edge_irq, NULL, NULL);
> +	return 0;
> +}
> +
> +static int apple_spmi_irq_domain_translate(struct irq_domain *irqd,
> +					struct irq_fwspec *fwspec,
> +					unsigned long *hwirq,
> +					unsigned int *type)
> +{
> +	u32 *args = fwspec->param;
> +
> +	if (fwspec->param_count != 2)
> +		return -EINVAL;
> +
> +	if (args[0] >= SPMI_NUM_PERIPHERAL_IRQS)
> +		return -EINVAL;
> +	*hwirq = args[0];
> +	*type = args[1] & IRQ_TYPE_SENSE_MASK;
> +	return 0;
> +}
> +
> +static int apple_spmi_irq_domain_alloc(struct irq_domain *irqd, unsigned int virq,
> +				unsigned int nr_irqs, void *arg)
> +{
> +	unsigned int type = IRQ_TYPE_NONE;
> +	struct irq_fwspec *fwspec = arg;
> +	irq_hw_number_t hwirq;
> +	int i, ret;
> +
> +	ret = apple_spmi_irq_domain_translate(irqd, fwspec, &hwirq, &type);
> +	if (ret)
> +		return ret;
> +
> +	if (hwirq + nr_irqs > SPMI_NUM_PERIPHERAL_IRQS)
> +		return -EINVAL;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		ret = apple_spmi_irq_domain_map(irqd, virq + i, hwirq + i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void apple_spmi_irq_domain_free(struct irq_domain *irqd, unsigned int virq,
> +				unsigned int nr_irqs)
> +{
> +	int i;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		struct irq_data *d = irq_domain_get_irq_data(irqd, virq + i);
> +
> +		irq_set_handler(virq + i, NULL);
> +		irq_domain_reset_irq_data(d);
> +	}
> +}
> +
> +static const struct irq_domain_ops apple_spmi_irq_domain_ops = {
> +	.translate	= apple_spmi_irq_domain_translate,
> +	.alloc		= apple_spmi_irq_domain_alloc,
> +	.free		= apple_spmi_irq_domain_free,
> +};
> +
> +static void apple_spmi_irq_handler(struct irq_desc *desc)
> +{
> +	struct apple_spmi *spmi = irq_desc_get_handler_data(desc);
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	bool handled = false;
> +	unsigned long val, offset, bit;
> +
> +	chained_irq_enter(chip, desc);
> +	val = readl(spmi->regs + SPMI_IRQ_ACK_BASE + (SPMI_IRQ_NOTIFY / 32) * 4);
> +	if (val & BIT(SPMI_IRQ_NOTIFY % 32)) {
> +		apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY);
> +		complete(&spmi->fifo_rx);
> +		handled = true;
> +	}
> +
> +	for (offset = 0; offset < SPMI_NUM_PERIPHERAL_IRQS / 8; offset += sizeof(val)) {
> +		val = readq(spmi->regs + SPMI_IRQ_ACK_BASE + offset);
> +		/**
> +		 * because of other masters in the bus, we're going to get a multitude of
> +		 * interrupts we're not interested in. irq_resolve_mapping isn't very
> +		 * optimized for the nonexistent path, so instead we mask with (a locally
> +		 * cached version of) the IRQ mask
> +		 */
> +		val &= spmi->irq_mask_cache[offset / sizeof(val)];
> +		for_each_set_bit(bit, &val, 64) {
> +			generic_handle_domain_irq(spmi->irqd, offset * 8 + bit);
> +			handled = true;
> +			val &= ~BIT(bit);

this is no longer necessary

> +		}
> +	}
> +	if (!handled)
> +		handle_bad_irq(desc);
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static void remove_chained_handler(void *data)
> +{
> +	unsigned int irq = (unsigned int)(uintptr_t)data;
> +
> +	irq_set_chained_handler_and_data(irq, NULL, NULL);
> +}
> +
> +static int apple_spmi_init_irq(struct platform_device *pdev,
> +			  struct apple_spmi *spmi, int irq)
> +{
> +	int ret;
> +	struct irq_domain_info info = {
> +		.fwnode		= pdev->dev.fwnode,
> +		.hwirq_max	= ~0U,
> +		.ops		= &apple_spmi_irq_domain_ops,
> +		.host_data	= spmi,
> +	};
> +
> +	raw_spin_lock_init(&spmi->irq_mask_lock);
> +
> +	for (size_t offset = 0; offset < SPMI_NUM_IRQS / 8; offset += 4) {
> +		writel(0, spmi->regs + SPMI_IRQ_MASK_BASE + offset);
> +		writel(U32_MAX, spmi->regs + SPMI_IRQ_ACK_BASE + offset);
> +	}
> +
> +	spmi->irqd = devm_irq_domain_instantiate(&pdev->dev, &info);
> +	if (IS_ERR(spmi->irqd))
> +		return PTR_ERR(spmi->irqd);
> +
> +	spmi->notify_irq = true;
> +	ret = devm_add_action(&pdev->dev, remove_chained_handler, (void *)(uintptr_t)spmi->irq);
> +	if (ret)
> +		return ret;
> +
> +	irq_set_chained_handler_and_data(spmi->irq, apple_spmi_irq_handler, spmi);
> +	apple_spmi_irq_unmask_raw(spmi, SPMI_IRQ_NOTIFY);

logically I'd set spmi->notify_irq only after unmasking it, not that it
matters as the spmi controller isn't registered yet.

> +
> +	return 0;
> +}
> +
>  static int apple_spmi_probe(struct platform_device *pdev)
>  {
>  	struct apple_spmi *spmi;
> @@ -200,6 +441,8 @@ 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);
> +	platform_set_drvdata(pdev, spmi);
>  
>  	spmi->regs = devm_platform_ioremap_resource(pdev, 0);
>  	if (IS_ERR(spmi->regs))
> @@ -211,6 +454,15 @@ static int apple_spmi_probe(struct platform_device *pdev)
>  	ctrl->write_cmd = spmi_write_cmd;
>  	ctrl->cmd = spmi_cmd;
>  
> +	spmi->irq = platform_get_irq_optional(pdev, 0);
> +	if (spmi->irq < 0 && spmi->irq != -ENXIO)
> +		return spmi->irq;
> +	if (spmi->irq >= 0) {
> +		ret = apple_spmi_init_irq(pdev, spmi, spmi->irq);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	ret = devm_spmi_controller_add(&pdev->dev, ctrl);
>  	if (ret)
>  		return dev_err_probe(&pdev->dev, ret,

Reviewed-by: Janne Grunau <j@jannau.net>

thanks, this looks better than the initial version.

Janne

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v4 4/6] spmi: apple: Implement remaining commands
  2026-08-05 12:19   ` Janne Grunau
@ 2026-08-05 16:54     ` Sasha Finkelstein
  0 siblings, 0 replies; 20+ messages in thread
From: Sasha Finkelstein @ 2026-08-05 16:54 UTC (permalink / raw)
  To: Janne Grunau
  Cc: Sven Peter, Neal Gompa, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, asahi, linux-arm-kernel,
	linux-kernel, devicetree, Alba Mendez

On Aug 5, 2026, at 14:19, Janne Grunau <j@jannau.net> wrote:
>> 
>> -static int spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>> -  u16 saddr, u8 *buf, size_t len)
>> +static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
>> +  u16 param, const u8 *buf, size_t len, u8 *ibuf, size_t ilen)
> 
> Instead of two sets of not very distinctive variable names ("buf"/"len",
> "ibuf"/ilen") please use just buf/len and a boolean to indicate whether
> the command is a read or write if you insists of routing all commands
> through a single function. This can be simply added as first condition
> in the "while" iterating over the write/read buffers.

Tried it, ended up not working, as the buffers differ in constness




^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2026-08-05 16:54 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05 10:11 [PATCH v4 0/6] spmi: apple: Additional commands and interrupt support Sasha Finkelstein
2026-08-05 10:11 ` [PATCH v4 1/6] dt-bindings: spmi: apple,spmi: Add t603x Sasha Finkelstein
2026-08-05 10:19   ` sashiko-bot
2026-08-05 12:01   ` Janne Grunau
2026-08-05 10:11 ` [PATCH v4 2/6] spmi: apple: Validate FIFO state Sasha Finkelstein
2026-08-05 10:21   ` sashiko-bot
2026-08-05 12:03   ` Janne Grunau
2026-08-05 10:11 ` [PATCH v4 3/6] spmi: apple: check transaction status Sasha Finkelstein
2026-08-05 10:27   ` sashiko-bot
2026-08-05 12:06   ` Janne Grunau
2026-08-05 10:11 ` [PATCH v4 4/6] spmi: apple: Implement remaining commands Sasha Finkelstein
2026-08-05 10:24   ` sashiko-bot
2026-08-05 12:19   ` Janne Grunau
2026-08-05 16:54     ` Sasha Finkelstein
2026-08-05 10:11 ` [PATCH v4 5/6] spmi: apple: lock around FIFOs Sasha Finkelstein
2026-08-05 10:25   ` sashiko-bot
2026-08-05 12:25   ` Janne Grunau
2026-08-05 10:11 ` [PATCH v4 6/6] spmi: apple: Add interrupt functionality Sasha Finkelstein
2026-08-05 10:22   ` sashiko-bot
2026-08-05 12:42   ` Janne Grunau

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox