From d505e03bff1eb9363187160f084a594061652f97 Mon Sep 17 00:00:00 2001 From: Dj <43033058+DjDeveloperr@users.noreply.github.com> Date: Mon, 26 Oct 2026 02:34:17 -0400 Subject: [PATCH] spmi: apple: Support T6050 generation-4 interrupts and recovery Add T6050 generation-4 interrupt banks and bounded command-FIFO recovery. Retain the current command buffers, IRQ teardown, and older-controller behavior. Source snapshot: t6050-gpu ee68abb105200e99636a5edfc647a5cfc1d89954 Integration base: 9cbdd0734d9e186c5f5f5949a21b909d10c2571b Signed-off-by: Dj <43033058+DjDeveloperr@users.noreply.github.com> --- .../devicetree/bindings/spmi/apple,spmi.yaml | 6 +- drivers/spmi/spmi-apple-controller.c | 93 ++++++++++++++---- tools/testing/selftests/aurora-apif/spmi.py | 97 +++++++++++++++++++ 3 files changed, 176 insertions(+), 20 deletions(-) create mode 100755 tools/testing/selftests/aurora-apif/spmi.py diff --git a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml index bb4940558fbdb..0b237ff41b977 100644 --- a/Documentation/devicetree/bindings/spmi/apple,spmi.yaml +++ b/Documentation/devicetree/bindings/spmi/apple,spmi.yaml @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) AND GPL-2.0-only %YAML 1.2 --- $id: http://devicetree.org/schemas/spmi/apple,spmi.yaml# @@ -36,7 +36,9 @@ properties: - apple,t6000-spmi - apple,t8112-spmi - const: apple,spmi - - const: apple,t8142-spmi + - enum: + - apple,t8142-spmi + - apple,t6050-spmi reg: maxItems: 1 diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c index e56017fbb9af2..1200b0c9dd95d 100644 --- a/drivers/spmi/spmi-apple-controller.c +++ b/drivers/spmi/spmi-apple-controller.c @@ -56,7 +56,9 @@ struct apple_spmi_hw { u32 command; u32 reply; u32 rx_empty; - bool legacy_irqs; + u32 irq_mask; + u32 irq_ack; + bool gen4; }; static const struct apple_spmi_hw apple_spmi_gen1 = { @@ -64,7 +66,8 @@ static const struct apple_spmi_hw apple_spmi_gen1 = { .command = SPMI_CMD_REG, .reply = SPMI_RSP_REG, .rx_empty = SPMI_RX_FIFO_EMPTY, - .legacy_irqs = true, + .irq_mask = SPMI_IRQ_MASK_BASE, + .irq_ack = SPMI_IRQ_ACK_BASE, }; static const struct apple_spmi_hw apple_spmi_gen4 = { @@ -72,6 +75,9 @@ static const struct apple_spmi_hw apple_spmi_gen4 = { .command = 0x210, .reply = 0x220, .rx_empty = BIT(30), + .irq_mask = 0x400, + .irq_ack = 0x600, + .gen4 = true, }; struct apple_spmi { @@ -89,21 +95,21 @@ struct apple_spmi { 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; + u32 __iomem *reg = spmi->regs + spmi->hw->irq_ack + (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; + u32 __iomem *reg = spmi->regs + spmi->hw->irq_mask + (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; + u32 __iomem *reg = spmi->regs + spmi->hw->irq_mask + (irq / 32) * 4; writel(readl(reg) | BIT(irq % 32), reg); } @@ -174,6 +180,43 @@ static int apple_spmi_wait_rx_not_empty(struct spmi_controller *ctrl) return 0; } +/* Gen4 resets the command queue through its control register. The native + * sequence disables the controller, drains responses, resets pending commands, + * then restores the previous enable state. Never write reset data to the FIFO. + */ +static int apple_spmi_recover(struct apple_spmi *spmi) +{ + u32 enable, status; + int ret = 0, remaining = 512; + + if (!spmi->hw->gen4) { + writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_ACT_REG); + goto ack; + } + enable = readl(spmi->regs); + writel(0, spmi->regs); + while (!(readl(spmi->regs + spmi->hw->status) & spmi->hw->rx_empty)) { + if (!remaining--) { + ret = -EIO; + goto restore; + } + readl(spmi->regs + spmi->hw->reply); + } + if (!(readl(spmi->regs + spmi->hw->status) & BIT(14))) { + writel(BIT(0), spmi->regs + 4); + ret = readl_poll_timeout(spmi->regs + 4, status, + !(status & BIT(0)), 50, 50000); + if (!ret && (readl(spmi->regs + spmi->hw->status) & + (BIT(14) | BIT(30))) != (BIT(14) | BIT(30))) + ret = -EIO; + } +restore: + writel(enable, spmi->regs); +ack: + apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY); + return ret; +} + static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u16 param, const u8 *buf_wr, size_t len_wr, u8 *buf_rd, size_t len_rd) { @@ -186,11 +229,9 @@ static int spmi_raw_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u16 param, guard(mutex)(&spmi->fifo_lock); if (spmi->prev_fail) { - /* Do not consume a late reply as the reply to a new command. */ - if (!spmi->hw->legacy_irqs) - return -EIO; - writel(SPMI_ACT_FIFO_FLUSH, spmi->regs + SPMI_ACT_REG); - apple_spmi_irq_ack_raw(spmi, SPMI_IRQ_NOTIFY); + ret = apple_spmi_recover(spmi); + if (ret) + return ret; spmi->prev_fail = false; } reinit_completion(&spmi->fifo_rx); @@ -314,7 +355,8 @@ static struct irq_chip apple_spmi_irq_chip = { .irq_unmask = apple_spmi_irq_unmask, .irq_ack = apple_spmi_irq_ack, .irq_set_type = apple_spmi_irq_set_type, - .flags = IRQCHIP_ONESHOT_SAFE, + /* The chained parent stays enabled; children need no separate wake op. */ + .flags = IRQCHIP_ONESHOT_SAFE | IRQCHIP_SKIP_SET_WAKE, }; static int apple_spmi_irq_domain_map(struct irq_domain *irqd, @@ -385,6 +427,15 @@ static const struct irq_domain_ops apple_spmi_irq_domain_ops = { .free = apple_spmi_irq_domain_free, }; +static u64 apple_spmi_irq_status(struct apple_spmi *spmi, unsigned long offset) +{ + /* Generation 4 exposes 32-bit IRQ banks, including in the guest grant. */ + if (spmi->hw->gen4) + return readl(spmi->regs + spmi->hw->irq_ack + offset) | + ((u64)readl(spmi->regs + spmi->hw->irq_ack + offset + 4) << 32); + return readq(spmi->regs + spmi->hw->irq_ack + offset); +} + static void apple_spmi_irq_handler(struct irq_desc *desc) { struct apple_spmi *spmi = irq_desc_get_handler_data(desc); @@ -393,7 +444,7 @@ static void apple_spmi_irq_handler(struct irq_desc *desc) unsigned long val, offset, bit; chained_irq_enter(chip, desc); - val = readl(spmi->regs + SPMI_IRQ_ACK_BASE + (SPMI_IRQ_NOTIFY / 32) * 4); + val = readl(spmi->regs + spmi->hw->irq_ack + (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); @@ -401,7 +452,7 @@ static void apple_spmi_irq_handler(struct irq_desc *desc) } for (offset = 0; offset < SPMI_NUM_PERIPHERAL_IRQS / 8; offset += sizeof(val)) { - val = readq(spmi->regs + SPMI_IRQ_ACK_BASE + offset); + val = apple_spmi_irq_status(spmi, 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 @@ -444,14 +495,15 @@ static int apple_spmi_init_irq(struct platform_device *pdev, 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); + writel(0, spmi->regs + spmi->hw->irq_mask + offset); + writel(U32_MAX, spmi->regs + spmi->hw->irq_ack + 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, apple_spmi_teardown_irq, spmi); if (ret) return ret; @@ -468,6 +520,7 @@ static int apple_spmi_probe(struct platform_device *pdev) struct apple_spmi *spmi; struct spmi_controller *ctrl; int ret; + struct resource *res; ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*spmi)); if (IS_ERR(ctrl)) @@ -481,6 +534,12 @@ static int apple_spmi_probe(struct platform_device *pdev) init_completion(&spmi->fifo_rx); platform_set_drvdata(pdev, spmi); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res || resource_size(res) < + max_t(u32, SPMI_ACT_REG + 4, + spmi->hw->irq_ack + DIV_ROUND_UP(SPMI_NUM_IRQS, 32) * 4)) + return -EINVAL; + spmi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spmi->regs)) return PTR_ERR(spmi->regs); @@ -495,9 +554,6 @@ static int apple_spmi_probe(struct platform_device *pdev) if (spmi->irq < 0 && spmi->irq != -ENXIO) return spmi->irq; if (spmi->irq >= 0) { - if (!spmi->hw->legacy_irqs) - return dev_err_probe(&pdev->dev, -EOPNOTSUPP, - "Generation 4 interrupt layout is not supported\n"); ret = apple_spmi_init_irq(pdev, spmi, spmi->irq); if (ret) return ret; @@ -512,6 +568,7 @@ static int apple_spmi_probe(struct platform_device *pdev) } static const struct of_device_id apple_spmi_match_table[] = { + { .compatible = "apple,t6050-spmi", .data = &apple_spmi_gen4 }, { .compatible = "apple,t8142-spmi", .data = &apple_spmi_gen4 }, { .compatible = "apple,t8103-spmi", .data = &apple_spmi_gen1 }, { .compatible = "apple,spmi", .data = &apple_spmi_gen1 }, diff --git a/tools/testing/selftests/aurora-apif/spmi.py b/tools/testing/selftests/aurora-apif/spmi.py new file mode 100755 index 0000000000000..d8bcceba1c913 --- /dev/null +++ b/tools/testing/selftests/aurora-apif/spmi.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Exercise generation-4 FIFO recovery using the production function.""" +from pathlib import Path +import shutil +import subprocess +import tempfile +from common import block + +ROOT = Path(__file__).resolve().parents[4] + + +def main(): + print("TAP version 13\n1..1", flush=True) + if not shutil.which("cc"): + print("ok 1 - SPMI FIFO recovery # SKIP cc unavailable") + return 4 + source = (ROOT / "drivers/spmi/spmi-apple-controller.c").read_text() + start = source.index("static int apple_spmi_recover(") + end = source.index("static int spmi_raw_cmd(", start) + program = r''' +#include +#include +#include +#include +typedef uint32_t u32; typedef uint64_t u64; +#define BIT(n) (1U << (n)) +#define SPMI_ACT_FIFO_FLUSH 1 +#define SPMI_ACT_REG 0xa4 +#define SPMI_IRQ_NOTIFY 256 +struct hw { bool gen4; u32 status, reply, rx_empty, irq_ack; }; +struct apple_spmi { unsigned char *regs; const struct hw *hw; }; +static unsigned char regs[0x800]; +static u32 status, enabled, reset; +static int reads, acks, poll_result, irq_reads, wide_reads; +static u32 readl(void *p) { + unsigned long off = (unsigned char *)p - regs; + if (off == 0) return enabled; + if (off == 0x200) return status; + if (off == 0x220) reads++; + if (off == 0x600) {irq_reads++;return 1;} + if (off == 0x604) {irq_reads++;return 2;} + return 0; +} +static u64 readq(void*p){wide_reads++;return 0x300000004ULL;} +static void writel(u32 v, void *p) { + unsigned long off = (unsigned char *)p - regs; + if (off == 0) enabled = v; + if (off == 4) reset = v; +} +static int poll(u32 *v) { + *v = 0; + if (!poll_result) status = BIT(14) | BIT(30); + return poll_result; +} +#define readl_poll_timeout(addr, val, cond, delay, timeout) poll(&(val)) +static void apple_spmi_irq_ack_raw(struct apple_spmi *s, u32 irq) { acks++; } +''' + source[start:end] + block(source, r"^static u64 apple_spmi_irq_status\(") + r''' +int main(void) { + struct hw hw = {true, 0x200, 0x220, BIT(30)}; + struct apple_spmi s = {regs, &hw}; + hw.irq_ack=0x600; + assert(apple_spmi_irq_status(&s,0)==0x200000001ULL); + assert(irq_reads==2 && wide_reads==0); + hw.gen4=false; + assert(apple_spmi_irq_status(&s,0)==0x300000004ULL); + assert(irq_reads==2 && wide_reads==1); + hw.gen4=true; + enabled = 7; status = BIT(14) | BIT(30); + assert(apple_spmi_recover(&s) == 0); + assert(enabled == 7 && reset == 0 && acks == 1); + status = 0; reads = 0; + assert(apple_spmi_recover(&s) == -EIO); + assert(enabled == 7 && reads == 512 && acks == 2); + status = BIT(30); poll_result = -ETIMEDOUT; + assert(apple_spmi_recover(&s) == -ETIMEDOUT); + assert(enabled == 7 && reset == 1 && acks == 3); + status = BIT(30); poll_result = 0; + assert(apple_spmi_recover(&s) == 0 && enabled == 7); + return 0; +} +''' + try: + with tempfile.TemporaryDirectory() as tmp: + src, binary = Path(tmp) / "test.c", Path(tmp) / "test" + src.write_text(program) + subprocess.run(["cc", "-std=gnu11", str(src), "-o", str(binary)], check=True) + subprocess.run([str(binary)], check=True) + except subprocess.CalledProcessError: + print("not ok 1 - SPMI FIFO recovery") + return 1 + print("ok 1 - SPMI FIFO recovery") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())