Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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':
Expand Down
43 changes: 34 additions & 9 deletions drivers/pinctrl/pinctrl-apple-gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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))
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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];
}

Expand Down Expand Up @@ -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")) {
Expand All @@ -515,18 +525,33 @@ 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);

if (of_property_read_u32(pdev->dev.of_node, "apple,npins", &npins))
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]),
Expand Down
27 changes: 27 additions & 0 deletions drivers/pinctrl/pinctrl-apple-irq.h
Original file line number Diff line number Diff line change
@@ -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 <linux/errno.h>
#include <linux/types.h>

/* 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
2 changes: 1 addition & 1 deletion tools/testing/selftests/aurora-apif/Makefile
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions tools/testing/selftests/aurora-apif/gpio.py
Original file line number Diff line number Diff line change
@@ -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 <assert.h>
#include <errno.h>
#include <stdint.h>
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())