diff --git a/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml index b8b4a24107cd1..f0ca3bb2e063f 100644 --- a/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml @@ -25,6 +25,7 @@ properties: - apple,t8122-pinctrl - apple,t8132-pinctrl - apple,t8140-pinctrl + - apple,t6050-pinctrl - const: apple,t8103-pinctrl - items: # Do not add additional SoC to this list. @@ -66,6 +67,15 @@ properties: minItems: 1 maxItems: 7 + apple,irq-group-base: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 6 + default: 0 + description: | + First interrupt group owned by this host. Interrupts lists consecutive + groups starting at this index. The loader must preserve any groups + owned by resident firmware. The range must end before group seven. + interrupt-controller: true '#interrupt-cells': diff --git a/drivers/pinctrl/pinctrl-apple-gpio.c b/drivers/pinctrl/pinctrl-apple-gpio.c index eee7f5014ecc1..9001ddccf6d09 100644 --- a/drivers/pinctrl/pinctrl-apple-gpio.c +++ b/drivers/pinctrl/pinctrl-apple-gpio.c @@ -30,6 +30,14 @@ #include "pinctrl-utils.h" #include "core.h" #include "pinmux.h" +#include "pinctrl-apple-irq.h" + +struct apple_gpio_pinctrl; + +struct apple_gpio_irq_group { + struct apple_gpio_pinctrl *pctl; + u8 index; +}; struct apple_gpio_pinctrl { struct device *dev; @@ -40,7 +48,8 @@ struct apple_gpio_pinctrl { struct pinctrl_desc pinctrl_desc; struct gpio_chip gpio_chip; - u8 irqgrps[]; + u8 irq_group_base; + struct apple_gpio_irq_group irqgrps[]; }; #define REG_GPIO(x) (4 * (x)) @@ -314,7 +323,7 @@ static unsigned int apple_gpio_irq_startup(struct irq_data *data) struct apple_gpio_pinctrl *pctl = gpiochip_get_data(chip); apple_gpio_set_reg(pctl, data->hwirq, REG_GPIOx_GRP, - FIELD_PREP(REG_GPIOx_GRP, 0)); + FIELD_PREP(REG_GPIOx_GRP, pctl->irq_group_base)); apple_gpio_direction_input(chip, data->hwirq); apple_gpio_irq_unmask(data); @@ -343,19 +352,18 @@ static int apple_gpio_irq_set_type(struct irq_data *data, unsigned int type) static void apple_gpio_irq_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); - u8 *grpp = irq_desc_get_handler_data(desc); - struct apple_gpio_pinctrl *pctl; + struct apple_gpio_irq_group *group = irq_desc_get_handler_data(desc); + struct apple_gpio_pinctrl *pctl = group->pctl; unsigned int pinh, pinl; unsigned long pending; struct gpio_chip *gc; - pctl = container_of(grpp - *grpp, typeof(*pctl), irqgrps[0]); gc = &pctl->gpio_chip; chained_irq_enter(chip, desc); for (pinh = 0; pinh < gc->ngpio; pinh += 32) { - pending = readl_relaxed(pctl->base + REG_IRQ(*grpp, pinh)); - for_each_set_bit(pinl, &pending, 32) + pending = readl_relaxed(pctl->base + REG_IRQ(group->index, pinh)); + for_each_set_bit(pinl, &pending, min_t(unsigned int, 32, gc->ngpio - pinh)) generic_handle_domain_irq(gc->irq.domain, pinh + pinl); } chained_irq_exit(chip, desc); @@ -475,7 +483,8 @@ static int apple_gpio_register(struct apple_gpio_pinctrl *pctl) goto out_free_irq_data; girq->parents[i] = ret; - pctl->irqgrps[i] = i; + pctl->irqgrps[i].pctl = pctl; + pctl->irqgrps[i].index = pctl->irq_group_base + i; irq_data[i] = &pctl->irqgrps[i]; } @@ -506,7 +515,8 @@ static int apple_gpio_pinctrl_probe(struct platform_device *pdev) static const char *pinmux_functions[] = { "gpio", "periph1", "periph2", "periph3" }; - unsigned int i, nirqs = 0; + unsigned int i, nirqs = 0, irq_group_base = 0; + struct resource *resource; int res; if (of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) { @@ -515,11 +525,21 @@ static int apple_gpio_pinctrl_probe(struct platform_device *pdev) nirqs = res; } + if (of_property_present(pdev->dev.of_node, "apple,irq-group-base")) { + res = of_property_read_u32(pdev->dev.of_node, "apple,irq-group-base", + &irq_group_base); + if (res) + return res; + } + if (irq_group_base > 6 || nirqs > 7 - irq_group_base) + return dev_err_probe(&pdev->dev, -EINVAL, "Invalid host IRQ groups\n"); + pctl = devm_kzalloc(&pdev->dev, struct_size(pctl, irqgrps, nirqs), GFP_KERNEL); if (!pctl) return -ENOMEM; pctl->dev = &pdev->dev; + pctl->irq_group_base = irq_group_base; pctl->gpio_chip.irq.num_parents = nirqs; dev_set_drvdata(&pdev->dev, pctl); @@ -527,6 +547,11 @@ static int apple_gpio_pinctrl_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, -EINVAL, "apple,npins property not found\n"); + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource || apple_gpio_irq_layout(npins, irq_group_base, nirqs, + resource_size(resource))) + return dev_err_probe(&pdev->dev, -EINVAL, "Invalid GPIO register aperture\n"); + pins = devm_kmalloc_array(&pdev->dev, npins, sizeof(pins[0]), GFP_KERNEL); pin_names = devm_kmalloc_array(&pdev->dev, npins, sizeof(pin_names[0]), diff --git a/drivers/pinctrl/pinctrl-apple-irq.h b/drivers/pinctrl/pinctrl-apple-irq.h new file mode 100644 index 0000000000000..ecfb1f95b7495 --- /dev/null +++ b/drivers/pinctrl/pinctrl-apple-irq.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2026 Aurora Silicon */ +#ifndef PINCTRL_APPLE_IRQ_H +#define PINCTRL_APPLE_IRQ_H + +#include +#include + +/* All pending banks must fit before registering the chained parent IRQs. */ +static inline int apple_gpio_irq_layout(u32 pins, u32 first, u32 count, + u64 aperture) +{ + u32 size; + + if (!pins || pins > 512 || first > 6 || count > 7 - first) + return -EINVAL; + size = pins * 4; + if (count) { + u32 last = 0x800 + (first + count - 1) * 0x40 + + ((pins - 1) / 32) * 4 + 4; + + if (last > size) + size = last; + } + return aperture < size ? -EINVAL : 0; +} +#endif diff --git a/tools/testing/selftests/aurora-apif/Makefile b/tools/testing/selftests/aurora-apif/Makefile index 1d64097501fbb..df91d578479e9 100644 --- a/tools/testing/selftests/aurora-apif/Makefile +++ b/tools/testing/selftests/aurora-apif/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -TEST_PROGS := dcp.py afk.py apif_mmio.py pmp.py guest.py spmi.py rtc.py display.py centauri.py wifi.py bluetooth.py +TEST_PROGS := gpio.py dcp.py afk.py apif_mmio.py pmp.py guest.py spmi.py rtc.py display.py centauri.py wifi.py bluetooth.py include ../lib.mk diff --git a/tools/testing/selftests/aurora-apif/gpio.py b/tools/testing/selftests/aurora-apif/gpio.py new file mode 100755 index 0000000000000..a90e6bc37f0fc --- /dev/null +++ b/tools/testing/selftests/aurora-apif/gpio.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Check production host GPIO IRQ-bank and aperture validation.""" +from pathlib import Path +import re +import shutil +import subprocess +import tempfile + +ROOT = Path(__file__).resolve().parents[4] + + +def main(): + print("TAP version 13\n1..1", flush=True) + if not shutil.which("cc"): + print("ok 1 - GPIO IRQ ownership bounds # SKIP cc unavailable") + return 4 + header = (ROOT / "drivers/pinctrl/pinctrl-apple-irq.h").read_text() + header = re.sub(r"^#include .*\n", "", header, flags=re.M) + program = r""" +#include +#include +#include +typedef uint32_t u32; +typedef uint64_t u64; +""" + program += header + program += r""" +int main(void) { + assert(!apple_gpio_irq_layout(128, 4, 3, 0x990)); + assert(apple_gpio_irq_layout(128, 4, 3, 0x98f) == -EINVAL); + assert(!apple_gpio_irq_layout(512, 0, 7, 0x9c0)); + assert(apple_gpio_irq_layout(512, 0, 7, 0x9bf) == -EINVAL); + assert(!apple_gpio_irq_layout(512, 6, 0, 0x800)); + assert(apple_gpio_irq_layout(512, 6, 0, 0x7ff) == -EINVAL); + assert(apple_gpio_irq_layout(0, 0, 1, UINT64_MAX) == -EINVAL); + assert(apple_gpio_irq_layout(513, 0, 1, UINT64_MAX) == -EINVAL); + assert(apple_gpio_irq_layout(32, 7, 0, UINT64_MAX) == -EINVAL); + assert(apple_gpio_irq_layout(32, 6, 2, UINT64_MAX) == -EINVAL); + assert(apple_gpio_irq_layout(32, UINT32_MAX, 1, UINT64_MAX) == -EINVAL); + assert(apple_gpio_irq_layout(32, 0, UINT32_MAX, UINT64_MAX) == -EINVAL); + return 0; +} +""" + with tempfile.TemporaryDirectory() as tmp: + source, binary = Path(tmp) / "gpio.c", Path(tmp) / "gpio" + source.write_text(program) + subprocess.run(["cc", "-std=gnu11", "-Wall", "-Wextra", "-Werror", + "-fsanitize=address,undefined", str(source), "-o", str(binary)], + check=True) + subprocess.run([str(binary)], check=True) + print("ok 1 - GPIO IRQ banks and apertures") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())