From: Florian Fainelli <florian.fainelli@broadcom.com>
To: linux-kernel@vger.kernel.org
Cc: "Florian Fainelli" <florian.fainelli@broadcom.com>,
"Hauke Mehrtens" <hauke@hauke-m.de>,
"Rafał Miłecki" <zajec5@gmail.com>,
"Broadcom internal kernel review list"
<bcm-kernel-feedback-list@broadcom.com>,
"Jonathan Corbet" <corbet@lwn.net>,
"Shuah Khan" <skhan@linuxfoundation.org>,
"Randy Dunlap" <rdunlap@infradead.org>,
"Will Deacon" <will@kernel.org>,
"Mark Rutland" <mark.rutland@arm.com>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Besar Wicaksono" <bwicaksono@nvidia.com>,
"Ilkka Koskinen" <ilkka@os.amperecomputing.com>,
linux-arm-kernel@lists.infradead.org (moderated list:BROADCOM
BCM5301X ARM ARCHITECTURE),
linux-doc@vger.kernel.org (open list:DOCUMENTATION),
linux-perf-users@vger.kernel.org (open list:ARM PMU PROFILING
AND DEBUGGING),
devicetree@vger.kernel.org (open list:OPEN FIRMWARE AND
FLATTENED DEVICE TREE BINDINGS)
Subject: [PATCH 2/3] perf: Add Broadcom BCM5301X AXI Trace Module counters
Date: Mon, 26 Oct 2026 14:11:50 -0700 [thread overview]
Message-ID: <20261026211151.3212237-3-florian.fainelli@broadcom.com> (raw)
In-Reply-To: <20261026211151.3212237-1-florian.fainelli@broadcom.com>
Expose unfiltered fixed counters through system-wide perf events. Keep
hardware free-running and use per-event baselines, locking, and periodic
32-bit delta accounting without resetting concurrent measurements.
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
.../admin-guide/perf/bcm5301x-axi-pmu.rst | 92 ++++
Documentation/admin-guide/perf/index.rst | 1 +
MAINTAINERS | 2 +
drivers/perf/Kconfig | 12 +
drivers/perf/Makefile | 1 +
drivers/perf/bcm5301x_axi_pmu.c | 450 ++++++++++++++++++
6 files changed, 558 insertions(+)
create mode 100644 Documentation/admin-guide/perf/bcm5301x-axi-pmu.rst
create mode 100644 drivers/perf/bcm5301x_axi_pmu.c
diff --git a/Documentation/admin-guide/perf/bcm5301x-axi-pmu.rst b/Documentation/admin-guide/perf/bcm5301x-axi-pmu.rst
new file mode 100644
index 000000000000..720f042dfbd0
--- /dev/null
+++ b/Documentation/admin-guide/perf/bcm5301x-axi-pmu.rst
@@ -0,0 +1,92 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Broadcom BCM5301X AXI Trace Module PMU
+====================================
+
+The BCM5301X AXI Trace Modules observe the two PL310 master interfaces and
+the Accelerator Coherency Port (ACP). Each instance registers a PMU named
+``bcm5301x_axi_<physical-base>``, for example ``bcm5301x_axi_19003000`` for
+PL310 M0, ``bcm5301x_axi_19004000`` for PL310 M1, and
+``bcm5301x_axi_19007000`` for ACP.
+
+Enable CONFIG_PERF_EVENTS and CONFIG_BCM5301X_AXI_PMU and provide an enabled
+device-tree node for each instance. Load ``bcm5301x_axi_pmu`` if built as a
+module. The driver owns the module exclusively; firmware or debug tools
+must not change its configuration while the driver is bound.
+
+Events and scope
+----------------
+
+The ``events`` and ``format`` directories under
+``/sys/bus/event_source/devices/bcm5301x_axi_*`` describe the ABI. Event IDs
+0 through 15 select fixed counters:
+
+==== ====================================
+ID Event
+==== ====================================
+0 write_commands
+1 read_commands
+2 write_address_cycles
+3 read_address_cycles
+4 write_data_cycles
+5 read_data_cycles
+6 write_response_cycles
+7 write_address_stall_cycles
+8 read_address_stall_cycles
+9 write_data_stall_cycles
+10 read_data_stall_cycles
+11 write_response_stall_cycles
+12 write_beats
+13 read_beats
+14 write_outstanding_cycles
+15 read_outstanding_cycles
+==== ====================================
+
+Active channel cycles count VALID asserted; stall cycles count VALID
+asserted while READY is deasserted. Outstanding cycles count cycles with
+more than zero outstanding commands. These are cycle counts, not the sum
+of outstanding requests. Counts cover all traffic at the observed port;
+PL310 hits do not necessarily generate traffic at its master interfaces.
+ACP can remain idle unless an accelerator uses that interface.
+
+All measurements are unfiltered and system-wide. The ``cpumask`` file
+identifies the CPU managing each PMU, not a CPU traffic filter. Per-task
+counting, privilege exclusions, sampling, configurable AXI filters,
+latency measurements, and ATB transaction trace capture are unsupported.
+Beat counts are reported as beats, without assuming a byte conversion.
+
+Each fixed counter can be allocated once at a time. Duplicate requests
+in a single group are rejected; separate events can be multiplexed by
+perf. Different counters on one PMU can run together. Groups spanning
+different hardware PMUs are rejected.
+
+The hardware counters run continuously while the driver is bound, with
+per-event baselines excluding periods when an event is stopped. Starting
+one event does not reset another event's hardware counter. There is no
+overflow interrupt. A timer extends the wrapping 32-bit counters to 64 bits
+every 100 ms while events run. At 500 MHz a counter incrementing once per
+cycle wraps in about 8.6 seconds. Accuracy requires servicing the timer
+before a full wrap; long interrupt stalls and suspend intervals can lose
+counts. Stop measurements before suspending the system.
+
+Testing
+-------
+
+Check discovery and count commands and beats over a memory workload::
+
+ modprobe bcm5301x_axi_pmu
+ ls /sys/bus/event_source/devices/bcm5301x_axi_*
+ perf list | grep bcm5301x_axi
+ perf stat -a -e '{bcm5301x_axi_19003000/read_commands/,bcm5301x_axi_19003000/read_beats/}' -- sleep 10
+
+Repeat during a workload whose working set exceeds the PL310 cache, such
+as a memory stress test. Compare with idle, test M1 separately, and expect
+ACP activity only with an ACP user. Network DMA traffic alone need not
+cross the monitored PL310 ports. Use ``perf stat -a -I 1000`` for interval
+counts and run for at least a minute under load to exercise counter wrap.
+
+Check that adding or removing a second, different counter leaves the first
+counter accumulating. Two requests for the same event in one group must
+fail. ``perf record`` and per-task ``perf stat`` must fail. If CPU hotplug
+is available, offline the CPU listed in ``cpumask`` while counting and
+verify that management moves to another online CPU and counting continues.
diff --git a/Documentation/admin-guide/perf/index.rst b/Documentation/admin-guide/perf/index.rst
index aa12708ddb96..6cf0010a3413 100644
--- a/Documentation/admin-guide/perf/index.rst
+++ b/Documentation/admin-guide/perf/index.rst
@@ -31,3 +31,4 @@ Performance monitor support
ampere_cspmu
mrvl-pem-pmu
fujitsu_uncore_pmu
+ bcm5301x-axi-pmu
diff --git a/MAINTAINERS b/MAINTAINERS
index b97f22107fbf..460725721bc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5220,12 +5220,14 @@ M: Rafał Miłecki <zajec5@gmail.com>
R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
+F: Documentation/admin-guide/perf/bcm5301x-axi-pmu.rst
F: Documentation/devicetree/bindings/perf/brcm,bcm5301x-axi-pmu.yaml
F: arch/arm/boot/dts/broadcom/bcm-ns.dtsi
F: arch/arm/boot/dts/broadcom/bcm470*
F: arch/arm/boot/dts/broadcom/bcm5301*
F: arch/arm/boot/dts/broadcom/bcm953012*
F: arch/arm/mach-bcm/bcm_5301x.c
+F: drivers/perf/bcm5301x_axi_pmu.c
BROADCOM BCM53573 ARM ARCHITECTURE
M: Florian Fainelli <florian.fainelli@broadcom.com>
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 245e7bb763b9..69674cc3f31f 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -6,6 +6,18 @@
menu "Performance monitor support"
depends on PERF_EVENTS
+config BCM5301X_AXI_PMU
+ tristate "Broadcom BCM5301X AXI Trace Module PMU"
+ depends on ARCH_BCM_5301X || COMPILE_TEST
+ depends on OF
+ help
+ Support system-wide perf counting on the BCM5301X AXI Trace Modules
+ monitoring the PL310 master interfaces and ACP. Supports commands,
+ beats, active/stall cycles, and outstanding-command cycles. Sampling
+ and transaction trace capture are not supported.
+
+ If compiled as a module, it will be called bcm5301x_axi_pmu.
+
config ARM_CCI_PMU
tristate "ARM CCI PMU driver"
depends on (ARM && CPU_V7) || ARM64
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index eb8a022dad9a..2a51a963b692 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_BCM5301X_AXI_PMU) += bcm5301x_axi_pmu.o
obj-$(CONFIG_ARM_CCI_PMU) += arm-cci.o
obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_ARM_CMN) += arm-cmn.o
diff --git a/drivers/perf/bcm5301x_axi_pmu.c b/drivers/perf/bcm5301x_axi_pmu.c
new file mode 100644
index 000000000000..15da137a4085
--- /dev/null
+++ b/drivers/perf/bcm5301x_axi_pmu.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Broadcom BCM5301X AXI Trace Module performance counters. */
+
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/cpuhotplug.h>
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+#define ATM_CONFIG 0x00
+#define ATM_STATUS 0x04
+#define ATM_CMD 0x0c
+#define ATM_CTRL_LOCAL BIT(0)
+#define ATM_CMDS_EN BIT(4)
+#define ATM_BEATS_EN BIT(6)
+#define ATM_BUSY_EN BIT(8)
+#define ATM_OUTS_EN BIT(14)
+#define ATM_COUNT_ENABLE (ATM_CMDS_EN | ATM_BEATS_EN | \
+ ATM_BUSY_EN | ATM_OUTS_EN)
+#define ATM_STATE_ENABLE (BIT(9) | BIT(10) | BIT(11) | BIT(13))
+#define ATM_STATE_MASK GENMASK(13, 9)
+#define ATM_CLEAR 0
+#define ATM_STOP 1
+#define ATM_START 2
+
+/* At most one increment per AXI cycle for the exposed counters. */
+#define ATM_POLL_INTERVAL ms_to_ktime(100)
+#define ATM_NR_COUNTERS 16
+
+static enum cpuhp_state bcm5301x_axi_hp_state;
+
+struct bcm5301x_axi_pmu {
+ struct pmu pmu;
+ void __iomem *base;
+ raw_spinlock_t lock;
+ struct hrtimer timer;
+ struct hlist_node hp_node;
+ struct perf_event *events[ATM_NR_COUNTERS];
+ unsigned int nr_active;
+ int cpu;
+};
+
+/* Event IDs select fixed counters, not programmable event selectors. */
+static const u16 bcm5301x_axi_offsets[ATM_NR_COUNTERS] = {
+ 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c,
+ 0x30, 0x34, 0x38, 0x3c, 0x58, 0x5c, 0x60, 0x64,
+};
+
+#define ATM_EVENT(_name, _id) \
+ static struct perf_pmu_events_attr event_attr_##_name = { \
+ .attr = __ATTR(_name, 0444, perf_event_sysfs_show, NULL), \
+ .id = _id, \
+ .event_str = "event=" __stringify(_id), \
+ }
+
+ATM_EVENT(write_commands, 0);
+ATM_EVENT(read_commands, 1);
+ATM_EVENT(write_address_cycles, 2);
+ATM_EVENT(read_address_cycles, 3);
+ATM_EVENT(write_data_cycles, 4);
+ATM_EVENT(read_data_cycles, 5);
+ATM_EVENT(write_response_cycles, 6);
+ATM_EVENT(write_address_stall_cycles, 7);
+ATM_EVENT(read_address_stall_cycles, 8);
+ATM_EVENT(write_data_stall_cycles, 9);
+ATM_EVENT(read_data_stall_cycles, 10);
+ATM_EVENT(write_response_stall_cycles, 11);
+ATM_EVENT(write_beats, 12);
+ATM_EVENT(read_beats, 13);
+ATM_EVENT(write_outstanding_cycles, 14);
+ATM_EVENT(read_outstanding_cycles, 15);
+
+static struct attribute *bcm5301x_axi_event_attrs[] = {
+ &event_attr_write_commands.attr.attr,
+ &event_attr_read_commands.attr.attr,
+ &event_attr_write_address_cycles.attr.attr,
+ &event_attr_read_address_cycles.attr.attr,
+ &event_attr_write_data_cycles.attr.attr,
+ &event_attr_read_data_cycles.attr.attr,
+ &event_attr_write_response_cycles.attr.attr,
+ &event_attr_write_address_stall_cycles.attr.attr,
+ &event_attr_read_address_stall_cycles.attr.attr,
+ &event_attr_write_data_stall_cycles.attr.attr,
+ &event_attr_read_data_stall_cycles.attr.attr,
+ &event_attr_write_response_stall_cycles.attr.attr,
+ &event_attr_write_beats.attr.attr,
+ &event_attr_read_beats.attr.attr,
+ &event_attr_write_outstanding_cycles.attr.attr,
+ &event_attr_read_outstanding_cycles.attr.attr,
+ NULL,
+};
+
+static const struct attribute_group bcm5301x_axi_events_group = {
+ .name = "events",
+ .attrs = bcm5301x_axi_event_attrs,
+};
+
+PMU_FORMAT_ATTR(event, "config:0-3");
+
+static struct attribute *bcm5301x_axi_format_attrs[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static const struct attribute_group bcm5301x_axi_format_group = {
+ .name = "format",
+ .attrs = bcm5301x_axi_format_attrs,
+};
+
+static struct bcm5301x_axi_pmu *to_axi_pmu(struct pmu *pmu)
+{
+ return container_of(pmu, struct bcm5301x_axi_pmu, pmu);
+}
+
+static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct bcm5301x_axi_pmu *axi = to_axi_pmu(dev_get_drvdata(dev));
+
+ return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpumask_of(READ_ONCE(axi->cpu))));
+}
+static DEVICE_ATTR_RO(cpumask);
+
+static struct attribute *bcm5301x_axi_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static const struct attribute_group bcm5301x_axi_group = {
+ .attrs = bcm5301x_axi_attrs,
+};
+
+static const struct attribute_group *bcm5301x_axi_attr_groups[] = {
+ &bcm5301x_axi_events_group,
+ &bcm5301x_axi_format_group,
+ &bcm5301x_axi_group,
+ NULL,
+};
+
+static u32 bcm5301x_axi_read_counter(struct bcm5301x_axi_pmu *axi, int idx)
+{
+ return readl(axi->base + bcm5301x_axi_offsets[idx]);
+}
+
+/* All counter state, including prev_count, is serialized by axi->lock. */
+static void bcm5301x_axi_update(struct bcm5301x_axi_pmu *axi,
+ struct perf_event *event)
+{
+ u32 now = bcm5301x_axi_read_counter(axi, event->hw.idx);
+ u32 prev = local64_read(&event->hw.prev_count);
+
+ local64_set(&event->hw.prev_count, now);
+ local64_add((u32)(now - prev), &event->count);
+}
+
+static enum hrtimer_restart bcm5301x_axi_timer(struct hrtimer *timer)
+{
+ struct bcm5301x_axi_pmu *axi = container_of(timer, typeof(*axi), timer);
+ enum hrtimer_restart ret = HRTIMER_NORESTART;
+ unsigned long flags;
+ int i;
+
+ raw_spin_lock_irqsave(&axi->lock, flags);
+ for (i = 0; i < ATM_NR_COUNTERS; i++) {
+ struct perf_event *event = axi->events[i];
+
+ if (event && !(event->hw.state & PERF_HES_STOPPED))
+ bcm5301x_axi_update(axi, event);
+ }
+ if (axi->nr_active) {
+ hrtimer_forward_now(timer, ATM_POLL_INTERVAL);
+ ret = HRTIMER_RESTART;
+ }
+ raw_spin_unlock_irqrestore(&axi->lock, flags);
+
+ return ret;
+}
+
+static int bcm5301x_axi_event_init(struct perf_event *event)
+{
+ struct bcm5301x_axi_pmu *axi = to_axi_pmu(event->pmu);
+ struct perf_event *sibling, *leader = event->group_leader;
+ unsigned long used = 0;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+ if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK ||
+ event->cpu < 0)
+ return -EOPNOTSUPP;
+ if (event->attr.config >= ATM_NR_COUNTERS || event->attr.config1 ||
+ event->attr.config2)
+ return -EINVAL;
+
+ /* A group cannot allocate the same fixed counter twice. */
+ if (!is_software_event(leader)) {
+ if (leader->pmu != event->pmu)
+ return -EINVAL;
+ __set_bit(leader->attr.config, &used);
+ }
+ for_each_sibling_event(sibling, leader) {
+ if (is_software_event(sibling))
+ continue;
+ if (sibling->pmu != event->pmu ||
+ test_and_set_bit(sibling->attr.config, &used))
+ return -EINVAL;
+ }
+ if (event != leader && test_bit(event->attr.config, &used))
+ return -EINVAL;
+
+ event->cpu = READ_ONCE(axi->cpu);
+ event->hw.idx = -1;
+ return 0;
+}
+
+static void bcm5301x_axi_start(struct perf_event *event, int flags)
+{
+ struct bcm5301x_axi_pmu *axi = to_axi_pmu(event->pmu);
+ unsigned long irqflags;
+
+ raw_spin_lock_irqsave(&axi->lock, irqflags);
+ if (event->hw.state & PERF_HES_STOPPED) {
+ local64_set(&event->hw.prev_count,
+ bcm5301x_axi_read_counter(axi, event->hw.idx));
+ event->hw.state = 0;
+ /* Unpinned: the timer can migrate when the managing CPU goes offline. */
+ if (!axi->nr_active++)
+ hrtimer_start(&axi->timer, ATM_POLL_INTERVAL, HRTIMER_MODE_REL);
+ }
+ raw_spin_unlock_irqrestore(&axi->lock, irqflags);
+}
+
+static void bcm5301x_axi_stop(struct perf_event *event, int flags)
+{
+ struct bcm5301x_axi_pmu *axi = to_axi_pmu(event->pmu);
+ unsigned long irqflags;
+
+ raw_spin_lock_irqsave(&axi->lock, irqflags);
+ if (!(event->hw.state & PERF_HES_STOPPED)) {
+ bcm5301x_axi_update(axi, event);
+ event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ axi->nr_active--;
+ }
+ /* The timer stops itself when no events are running. */
+ raw_spin_unlock_irqrestore(&axi->lock, irqflags);
+}
+
+static void bcm5301x_axi_read(struct perf_event *event)
+{
+ struct bcm5301x_axi_pmu *axi = to_axi_pmu(event->pmu);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&axi->lock, flags);
+ if (!(event->hw.state & PERF_HES_STOPPED))
+ bcm5301x_axi_update(axi, event);
+ raw_spin_unlock_irqrestore(&axi->lock, flags);
+}
+
+static int bcm5301x_axi_add(struct perf_event *event, int flags)
+{
+ struct bcm5301x_axi_pmu *axi = to_axi_pmu(event->pmu);
+ unsigned int idx = event->attr.config;
+ unsigned long irqflags;
+
+ raw_spin_lock_irqsave(&axi->lock, irqflags);
+ if (axi->events[idx]) {
+ raw_spin_unlock_irqrestore(&axi->lock, irqflags);
+ return -EAGAIN;
+ }
+ axi->events[idx] = event;
+ event->hw.idx = idx;
+ event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ raw_spin_unlock_irqrestore(&axi->lock, irqflags);
+
+ if (flags & PERF_EF_START)
+ bcm5301x_axi_start(event, PERF_EF_RELOAD);
+ perf_event_update_userpage(event);
+ return 0;
+}
+
+static void bcm5301x_axi_del(struct perf_event *event, int flags)
+{
+ struct bcm5301x_axi_pmu *axi = to_axi_pmu(event->pmu);
+ unsigned long irqflags;
+
+ bcm5301x_axi_stop(event, PERF_EF_UPDATE);
+ raw_spin_lock_irqsave(&axi->lock, irqflags);
+ axi->events[event->hw.idx] = NULL;
+ event->hw.idx = -1;
+ raw_spin_unlock_irqrestore(&axi->lock, irqflags);
+ perf_event_update_userpage(event);
+}
+
+static int bcm5301x_axi_offline(unsigned int cpu, struct hlist_node *node)
+{
+ struct bcm5301x_axi_pmu *axi = hlist_entry(node, typeof(*axi), hp_node);
+ unsigned int target;
+
+ if (cpu != READ_ONCE(axi->cpu))
+ return 0;
+ target = cpumask_any_but(cpu_online_mask, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+ perf_pmu_migrate_context(&axi->pmu, cpu, target);
+ WRITE_ONCE(axi->cpu, target);
+ return 0;
+}
+
+static void bcm5301x_axi_hw_stop(struct bcm5301x_axi_pmu *axi)
+{
+ writel(ATM_STOP, axi->base + ATM_CMD);
+ /* Flush the posted command before the resource is unmapped. */
+ readl(axi->base + ATM_STATUS);
+}
+
+static int bcm5301x_axi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm5301x_axi_pmu *axi;
+ struct resource *res;
+ char *name;
+ u32 status;
+ int ret;
+
+ axi = devm_kzalloc(dev, sizeof(*axi), GFP_KERNEL);
+ if (!axi)
+ return -ENOMEM;
+ axi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(axi->base))
+ return PTR_ERR(axi->base);
+ if (resource_size(res) < SZ_4K)
+ return -EINVAL;
+ name = devm_kasprintf(dev, GFP_KERNEL, "bcm5301x_axi_%llx",
+ (unsigned long long)res->start);
+ if (!name)
+ return -ENOMEM;
+
+ raw_spin_lock_init(&axi->lock);
+ hrtimer_setup(&axi->timer, bcm5301x_axi_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ axi->pmu = (struct pmu) {
+ .module = THIS_MODULE,
+ .parent = dev,
+ .task_ctx_nr = perf_invalid_context,
+ .capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
+ .attr_groups = bcm5301x_axi_attr_groups,
+ .event_init = bcm5301x_axi_event_init,
+ .add = bcm5301x_axi_add,
+ .del = bcm5301x_axi_del,
+ .start = bcm5301x_axi_start,
+ .stop = bcm5301x_axi_stop,
+ .read = bcm5301x_axi_read,
+ };
+
+ /* Take exclusive local ownership; disable tracing, filtering and saturation. */
+ writel(ATM_CTRL_LOCAL, axi->base + ATM_CONFIG);
+ writel(ATM_STOP, axi->base + ATM_CMD);
+ ret = readl_poll_timeout(axi->base + ATM_STATUS, status,
+ !(status & ATM_STATE_MASK), 1, 1000);
+ if (ret)
+ return dev_err_probe(dev, ret, "Counters did not stop\n");
+ writel(ATM_CLEAR, axi->base + ATM_CMD);
+ writel(ATM_CTRL_LOCAL | ATM_COUNT_ENABLE, axi->base + ATM_CONFIG);
+ writel(ATM_START, axi->base + ATM_CMD);
+ ret = readl_poll_timeout(axi->base + ATM_STATUS, status,
+ (status & ATM_STATE_MASK) == ATM_STATE_ENABLE, 1, 1000);
+ if (ret) {
+ bcm5301x_axi_hw_stop(axi);
+ return dev_err_probe(dev, ret, "Counters did not start\n");
+ }
+ /*
+ * Keep fixed counters free-running. Per-event baselines isolate sessions
+ * without clearing or stopping counters used by another perf event.
+ */
+ cpus_read_lock();
+ axi->cpu = cpumask_first(cpu_online_mask);
+ ret = perf_pmu_register(&axi->pmu, name, -1);
+ if (!ret) {
+ ret = cpuhp_state_add_instance_nocalls_cpuslocked(bcm5301x_axi_hp_state,
+ &axi->hp_node);
+ if (ret)
+ perf_pmu_unregister(&axi->pmu);
+ }
+ cpus_read_unlock();
+ if (ret) {
+ hrtimer_cancel(&axi->timer);
+ bcm5301x_axi_hw_stop(axi);
+ return ret;
+ }
+ platform_set_drvdata(pdev, axi);
+ return 0;
+}
+
+static void bcm5301x_axi_remove(struct platform_device *pdev)
+{
+ struct bcm5301x_axi_pmu *axi = platform_get_drvdata(pdev);
+
+ cpuhp_state_remove_instance_nocalls(bcm5301x_axi_hp_state, &axi->hp_node);
+ perf_pmu_unregister(&axi->pmu);
+ hrtimer_cancel(&axi->timer);
+ bcm5301x_axi_hw_stop(axi);
+}
+
+static const struct of_device_id bcm5301x_axi_of_match[] = {
+ { .compatible = "brcm,bcm5301x-axi-pmu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bcm5301x_axi_of_match);
+
+static struct platform_driver bcm5301x_axi_driver = {
+ .probe = bcm5301x_axi_probe,
+ .remove = bcm5301x_axi_remove,
+ .driver = {
+ .name = "bcm5301x-axi-pmu",
+ .of_match_table = bcm5301x_axi_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init bcm5301x_axi_init(void)
+{
+ int ret;
+
+ ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "perf/bcm5301x_axi:online",
+ NULL, bcm5301x_axi_offline);
+ if (ret < 0)
+ return ret;
+ bcm5301x_axi_hp_state = ret;
+ ret = platform_driver_register(&bcm5301x_axi_driver);
+ if (ret)
+ cpuhp_remove_multi_state(bcm5301x_axi_hp_state);
+ return ret;
+}
+module_init(bcm5301x_axi_init);
+
+static void __exit bcm5301x_axi_exit(void)
+{
+ platform_driver_unregister(&bcm5301x_axi_driver);
+ cpuhp_remove_multi_state(bcm5301x_axi_hp_state);
+}
+module_exit(bcm5301x_axi_exit);
+
+MODULE_DESCRIPTION("Broadcom BCM5301X AXI Trace Module PMU");
+MODULE_LICENSE("GPL");
--
2.53.0
next prev parent reply other threads:[~2026-10-26 21:12 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 21:11 [PATCH 0/3] Support for BCM5301X AXI trace PMUs Florian Fainelli
2026-10-26 21:11 ` [PATCH 1/3] dt-bindings: perf: Describe the Broadcom BCM5301X AXI PMU Florian Fainelli
2026-10-26 21:11 ` Florian Fainelli [this message]
2026-10-26 21:11 ` [PATCH 3/3] ARM: dts: BCM5301X: Enable AXI PMUs Florian Fainelli
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261026211151.3212237-3-florian.fainelli@broadcom.com \
--to=florian.fainelli@broadcom.com \
--cc=bcm-kernel-feedback-list@broadcom.com \
--cc=bwicaksono@nvidia.com \
--cc=conor+dt@kernel.org \
--cc=corbet@lwn.net \
--cc=devicetree@vger.kernel.org \
--cc=hauke@hauke-m.de \
--cc=ilkka@os.amperecomputing.com \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=rdunlap@infradead.org \
--cc=robh@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=will@kernel.org \
--cc=zajec5@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®