[PATCH v14 0/3] perf: Add Raspberry Pi AXI PMU driver
HOTtoday
From: Ian Rogers <irogers@google.com>
Date: 2026-10-02 17:58:12
Also in:
linux-devicetree, linux-perf-users, lkml
Revision v14 of 13 in this series.
Revisions (13)
Motivation & Background
==============================
Currently, Linux lacks a standard perf-API compatible driver for the
Broadcom AXI performance counter blocks on Raspberry Pi platforms. Prior
out-of-tree vendor solutions relied on custom debugfs nodes and ad-hoc
kthreads, preventing integration with standard Linux perf tooling (`perf stat`,
`perf list`, etc.).
This driver implements standard `struct pmu` hardware uncore callbacks
under `drivers/perf/`, exposing human-readable sysfs event aliases for all
11 hardware metrics per bus (`atwait`, `atrans`, `amax`, `wwait`, `wtrans`,
`wmax`, `rwait`, `rtrans`, `rmax`, `rpend`, `ratrans`) and 6-bit AXI master
ID filtering (`filter=0..50`) directly to user space.
Key Architectural Improvements & Features
==============================
1. Standard Linux Perf Integration:
- Exposes uncore AXI interconnect events via
`/sys/bus/event_source/devices/rpi_axi_pmu/`.
- Supports up to 3 concurrently monitored buses per monitor
(`NUM_BUS_WATCHER_RESULTS = 3`), with bus-watcher reference counting
so all 11 counter metrics on the same `(monitor, bus, filter)` tuple
share a single hardware watcher without multiplexing.
- Enforces atomic group validation (`rpi_axi_pmu__validate_group`) to
reject event groups requiring more than 3 watchers per monitor at
`perf_event_open()` time, while supporting time-multiplexing across
more than 3 ungrouped buses.
- Supports hardware counter accumulation (`local64_add`) using full
32-bit unsigned counter reads (`u32`) and periodic hrtimer polling
(`POLL_INTERVAL_MS = 100`) to prevent counter saturation.
2. CPU Hotplug Support (`cpuhp`):
- Registers dynamic CPU hotplug callbacks (`CPUHP_AP_ONLINE_DYN` via
`cpuhp_state_add_instance()`).
- Automatically migrates PMU context (`perf_pmu_migrate_context`) to an
online CPU core when the designated CPU goes offline.
3. Hybrid Memory-Mapped & Mailbox Work Queue Architecture:
- System Monitor (MMIO): Performs fast atomic-safe memory reads directly
mapped over ARM physical memory space (`MON__SYSTEM`).
- VPU Monitor (Mailbox IPC): For Broadcom BCM2835-BCM2711 platforms
(RPi 1-4) when configured via `system-only`, VideoCore VPU monitor
(`MON__VPU`) mailbox IPC calls are offloaded to process context via
`schedule_work(&pmu->mailbox_work)` and serialized with an atomic
snapshot cache (`cached_values`), avoiding sleeping in atomic/hrtimer
context. The firmware node is discovered via
`of_find_compatible_node(NULL, NULL, "raspberrypi,bcm2835-firmware")`
and untranslated VideoCore bus addresses are read from the `reg`
property via `of_property_read_u32_index()`.
4. Per-SoC Generation Support:
- Patch 1 documents the Device Tree bindings (`brcm,bcm2835-axiperf`,
`brcm,bcm2711-axiperf`, `brcm,bcm2712-axiperf`).
- Patch 2 adds core driver support for Broadcom BCM2835 and BCM2711 with
distinct per-SoC bus and AXI master ID filter tables matching the
hardware topology of each generation.
- Patch 3 expands support for Broadcom BCM2712 (Raspberry Pi 5), adding
the BCM2712 System monitor pipelines (`DISPLAY_TOP`, `ARGON_TOP`,
`BSTM_TOP`, `PCIE_01`, `XPT`, `ARM`, `SYSTEM_L2`, etc.) and 6-bit AXI
master ID filter tables (51 filter IDs, `GENMASK(13, 8)`).
Hardware Validation
==============================
The driver has been validated on Raspberry Pi 400 (BCM2711) and Raspberry
Pi 5 (BCM2712). Example on Raspberry Pi 5 monitoring 17 events across 3
buses (`bstm_top`, `arm`, `system_l2`) simultaneously without multiplexing:
$ sudo perf stat -a \
-e rpi_axi_pmu/bstm_top_atwait/,rpi_axi_pmu/bstm_top_atrans/,rpi_axi_pmu/bstm_top_amax/ \
-e rpi_axi_pmu/bstm_top_wwait/,rpi_axi_pmu/bstm_top_wtrans/,rpi_axi_pmu/bstm_top_wmax/ \
-e rpi_axi_pmu/bstm_top_rwait/,rpi_axi_pmu/bstm_top_rtrans/,rpi_axi_pmu/bstm_top_rmax/ \
-e rpi_axi_pmu/bstm_top_rpend/,rpi_axi_pmu/bstm_top_ratrans/ \
-e rpi_axi_pmu/arm_rtrans/,rpi_axi_pmu/arm_wtrans/,rpi_axi_pmu/arm_rmax/,rpi_axi_pmu/arm_wmax/ \
-e rpi_axi_pmu/system_l2_rtrans/,rpi_axi_pmu/system_l2_wtrans/ \
-- python3 -c 'a = bytearray(256 * 1024 * 1024); b = bytearray(a)'
Performance counter stats for 'system wide':
0 rpi_axi_pmu/bstm_top_atwait/
64 rpi_axi_pmu/bstm_top_atrans/
0 rpi_axi_pmu/bstm_top_amax/
0 rpi_axi_pmu/bstm_top_wwait/
0 rpi_axi_pmu/bstm_top_wtrans/
0 rpi_axi_pmu/bstm_top_wmax/
0 rpi_axi_pmu/bstm_top_rwait/
256 rpi_axi_pmu/bstm_top_rtrans/
0 rpi_axi_pmu/bstm_top_rmax/
190 rpi_axi_pmu/bstm_top_rpend/
64 rpi_axi_pmu/bstm_top_ratrans/
10,987,780 rpi_axi_pmu/arm_rtrans/
17,265,030 rpi_axi_pmu/arm_wtrans/
2,519 rpi_axi_pmu/arm_rmax/
1,093 rpi_axi_pmu/arm_wmax/
0 rpi_axi_pmu/system_l2_rtrans/
0 rpi_axi_pmu/system_l2_wtrans/
0.165352897 seconds time elapsed
Additionally verified on Raspberry Pi 5:
- 6-bit AXI master ID filtering (`filter=0..50` accepted, with active
traffic on `filter=16..23` and `filter=32..43` confirming bit 5 is
decoded in BCM2712 hardware, and `filter=51` rejected with `-EINVAL`).
- Time-multiplexing across all 13 BCM2712 buses and atomic group rejection
(`-EINVAL`) when grouping 4 distinct buses.
- Live CPU hotplug context migration when offlining `CPU0`.
Changes since v13
==============================
Addresses feedback from the upstream v13 review as well as downstream
review on https://github.com/raspberrypi/linux/pull/7571 (with the
corresponding updates tracked in
https://github.com/raspberrypi/linux/pull/7671):
- Device Tree Bindings (Patch 1):
- Removed the `firmware` phandle property and `allOf` conditional from
`brcm,bcm2835-axiperf.yaml` per Krzysztof Kozlowski's review; the
driver now locates `raspberrypi,bcm2835-firmware` via
`of_find_compatible_node()`.
- Set `reg` `minItems: 2` (`MON_SYSTEM` and `MON_VPU`) and updated the
example to use the BCM2712 address map (`0x7c012800` / `0x7e000000`).
- Core Driver & SoC Support (Patches 2 & 3):
- Removed all `LINUX_VERSION_CODE` conditional checks per Uwe
Kleine-König and Florian Fainelli.
- Separated BCM2835, BCM2711, and BCM2712 bus and AXI master ID filter
tables (`CHIP_BCM2835`, `CHIP_BCM2711`, `CHIP_BCM2712`) to match the
hardware definitions per https://github.com/raspberrypi/linux/pull/7571.
- Fixed VPU mailbox base address lookup to read untranslated 32-bit
VideoCore peripheral addresses via `of_property_read_u32_index()`
instead of CPU-translated `platform_get_resource()` addresses, and
skipped mapping `MON_VPU` on BCM2712 (`CHIP_BCM2712`).
- Widened the AXI master ID filter field from 5 bits to 6 bits
(`GENMASK(13, 8)` in hardware, `config:10-15` in perf attr) to support
all 51 BCM2712 filter IDs without truncation.
- Replaced 31-bit counter masking (`& 0x7FFFFFFF`) with full 32-bit
unsigned reads (`u32`) and removed speculative `scale="32"` /
`unit="Bytes"` sysfs attributes so raw transaction, cycle, and latency
counts are reported accurately.
- Added bus-watcher reference counting and
`rpi_axi_pmu__validate_group()` so multiple metrics on the same
`(monitor, bus, filter)` share a single hardware watcher and
unschedulable event groups fail at `perf_event_open()` with `-EINVAL`.
- Switched CPU hotplug registration to exported
`cpuhp_state_add_instance()` with an online callback
(`rpi_axi_pmu_online_cpu`) and updated `cpumask_show()` to use
`sysfs_emit()`.
- Fixed `Kconfig` dependencies (`ARCH_BCM2835`, `RASPBERRYPI_FIRMWARE`,
and `!RPI_AXIPERF`).
Ian Rogers (3):
dt-bindings: perf: Add Broadcom Raspberry Pi AXI PMU definition
perf: Add Raspberry Pi BCM2835 AXI PMU driver
perf: Add Raspberry Pi 5 (BCM2712) AXI PMU support
.../bindings/perf/brcm,bcm2835-axiperf.yaml | 71 +
drivers/perf/Kconfig | 13 +
drivers/perf/Makefile | 1 +
drivers/perf/rpi_axi_pmu.c | 3397 +++++++++++++++++
4 files changed, 3482 insertions(+)
create mode 100644 Documentation/devicetree/bindings/perf/brcm,bcm2835-axiperf.yaml
create mode 100644 drivers/perf/rpi_axi_pmu.c
--
2.56.0.rc1.315.gc6ed9934b7-goog