On 29/06/2026 08:10, Shivani Nittor wrote:
> Introduce a DTS-based PMU driver that discovers PMU information
> from the device tree and registers a PowerPC PMU instance at
> runtime.
>
> The driver parses basic PMU properties such as the number of
> counters and MMCR register definitions, creates sysfs event
> entries from the device tree event descriptions, and registers
> the PMU with the perf subsystem.
>
> To support dynamic PMU registration, add PMU unregistration
> support and create a platform device for PMU nodes described
> in the device tree.
>
> This forms the foundation for moving PMU descriptions out of
> architecture-specific kernel code and into device tree data.
>
> The driver implements the following functionality:
>
> Device Tree Parsing:
> - Reads PMU properties including counter count (nr_pmc), PMU name,
> version, and platform information
> - Parses MMCR (Monitor Mode Control Register) definitions from the
> sprs/mmcr node hierarchy, storing the MMCR register SPRNs
> - Extracts event definitions from the events node, supporting both
> 32-bit and 64-bit event codes
>
> Dynamic Event Registration:
> - Creates sysfs event entries dynamically from device tree event
> descriptions
> - Generates event attributes with format "event=0x<code>" for each
> discovered event
> - Exposes events under /sys/devices/<pmu-name>/events/
>
> PMU Registration:
> - Registers a power_pmu instance with the perf subsystem using the
> device tree-provided PMU name
> - Adds PMU unregistration support to enable proper cleanup
> - Creates platform device for "ibm,power-pmu" compatible nodes
>
> Sysfs Interface:
> - Provides format attributes (event, pmcsel) under
> /sys/devices/<pmu-name>/format/
> - Exposes device tree debug information (nr_pmc) under
> /sys/devices/<pmu-name>/dt/
Where did you document the ABI?
...
> +static struct power_pmu dts_pmu = {
> + .name = "cpu_dts",
> + .n_counter = MAX_PMU_COUNTERS,
> + .attr_groups = pmu_dts_attr_groups,
> +};
> +
> +/* Device Tree match */
That's obvious. Can of_device_id be something else than DT match?
> +static const struct of_device_id pmu_dts_of_match[] = {
> + { .compatible = "ibm,power-pmu" },
Where is ABI documented?
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, pmu_dts_of_match);
> +
> +/* Probe function */
> +static int pmu_dts_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node, *child, *events_np;
> + struct device_node *sprs_np, *mmcr_np, *mmcr_child;
> + struct pmu_dts_event *evt;
> + u64 code;
> + u32 code32, sprn;
> + u32 code64[2];
> + u32 code128[4];
> + int cells;
> + const char *str;
> +
> + pr_info("PMU DTS probe node = %s\n", np->full_name);
This does not look like useful printk message. Drivers should be silent
on success:
https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/coding-style.rst#L913
https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/debugging/driver_development_debugging_guide.rst#L79
> +
> + if (!of_property_present(np, "nr_pmc")) {
> +
> + for_each_child_of_node(np, child) {
> + pr_info("child node = %s\n", child->full_name);
Why are you sending debugging code?
> +
> + if (of_property_present(child, "nr_pmc")) {
> + np = child;
> + break;
> + }
> + }
> + }
> +
> + if (of_property_read_u32(np, "nr_pmc", &pmu_dts_nr_pmc)) {
> + pr_err("pmu_dts: nr_pmc not found in %s\n", np->full_name);
Why isn't this using dev interface?
> + return -EINVAL;
> + }
> +
> + if (!of_property_read_string(np, "pmu-name", &str))
> + pr_info("PMU Name: %s\n", str);
> +
> + if (!of_property_read_string(np, "pmu-version", &str))
> + pr_info("PMU Version: %s\n", str);
> +
> + if (!of_property_read_string(np, "platform", &str))
No, you cannot just add bunch of undocumented ABI and call it a day.
> + pr_info("Platform: %s\n", str);
> +
> + sprs_np = of_get_child_by_name(np, "sprs");
> + if (!sprs_np) {
> + pr_err("pmu_dts: no sprs node\n");
> + return -EINVAL;
> + }
> +
> + mmcr_np = of_get_child_by_name(sprs_np, "mmcr");
> + if (!mmcr_np) {
> + pr_err("pmu_dts: no mmcr node\n");
> + return -EINVAL;
> + }
> +
> + mmcr_count = 0;
> + for_each_child_of_node(mmcr_np, mmcr_child) {
> +
> + if (of_property_read_u32(mmcr_child, "sprn", &sprn))
> + continue;
> +
> + mmcr_regs_sprs[mmcr_count++] = sprn;
> + pr_info("pmu_dts: MMCR[%d] = %u (%s)\n", mmcr_count - 1,
> + sprn, mmcr_child->name);
> +
> + if (mmcr_count >= MAX_MMCR)
> + break;
Where is cleanup?
> + }
> +
> + if (!mmcr_count) {
> + pr_err("pmu_dts: no MMCR SPRs found\n");
> + return -EINVAL;
> + }
> +
> + /* Parse events */
> + events_np = of_get_child_by_name(np, "events");
> + if (!events_np) {
> + pr_err("pmu_dts: no events node found\n");
> + return -EINVAL;
> + }
> +
> + dts_event_count = 0;
> + for_each_child_of_node(events_np, child) {
> + if (!of_device_is_available(child))
Why are you open-coding available variant of loop?
> + continue;
> +
> + cells = of_property_count_u32_elems(child, "event_code");
> + if (cells == 1) {
> + if (of_property_read_u32(child, "event_code", &code32))
> + continue;
> +
> + code = code32;
> +
> + } else if (cells == 2) {
> + if (of_property_read_u32_array(child, "event_code",
> code64, 2))
> + continue;
> + code = ((u64)code64[0] << 32) | code64[1];
> +
> + } else if (cells == 4) {
> + if (of_property_read_u32_array(child, "event_code",
> code128, 4))
> + continue;
> + code = ((u64)code128[1] << 32) | code128[3];
> +
> + } else {
> + pr_warn("pmu_dts: invalid event_code for %s\n",
> child->name);
> + continue;
> + }
> +
> + evt = kzalloc(sizeof(*evt), GFP_KERNEL);
> + if (!evt)
> + continue;
> +
> + snprintf(evt->name, sizeof(evt->name), "%s", child->name);
> + snprintf(evt->config, sizeof(evt->config), "event=0x%llx",
> code);
> +
> + sysfs_attr_init(&evt->attr.attr);
> + evt->attr.attr.name = evt->name;
> + evt->attr.attr.mode = 0444;
> + evt->attr.show = pmu_dts_event_show;
> + dts_events[dts_event_count] = evt;
> + pmu_dts_events_attrs[dts_event_count] = &evt->attr.attr;
> + dts_event_count++;
> +
> + if (dts_event_count >= MAX_DTS_EVENTS)
> + break;
And node cleanup?
> + }
> + pmu_dts_events_attrs[dts_event_count] = NULL;
> +
> + /* Register PMU */
No.
> + pr_info("pmu_dts: registering PMU\n");
No.
> + return register_power_pmu(&dts_pmu);
> +}
> +
> +/* Platform driver */
This is ridicilous comment.
> +static struct platform_driver pmu_dts_driver = {
> + .probe = pmu_dts_probe,
> + .driver = {
> + .name = "pmu_dts",
> + .of_match_table = pmu_dts_of_match,
> + },
> +};
> +
> +static int __init pmu_dts_init(void)
> +{
> + pr_info("pmu_dts: init\n");
NAK, init functions do not print anything. RFC does not mean you can
send debugging code with poor quality and...
> + return platform_driver_register(&pmu_dts_driver);
> +}
> +
> +static void __exit pmu_dts_exit(void)
> +{
> + pr_info("pmu_dts: exit\n");
> + platform_driver_unregister(&pmu_dts_driver);
> + unregister_power_pmu(&dts_pmu);
> +}
> +module_init(pmu_dts_init);
> +module_exit(pmu_dts_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Shivani Nittor");
> +MODULE_DESCRIPTION("PMU DTS driver");
> diff --git a/arch/powerpc/perf/internal.h b/arch/powerpc/perf/internal.h
> index a70ac471a5a5..11154ee31f8e 100644
> --- a/arch/powerpc/perf/internal.h
> +++ b/arch/powerpc/perf/internal.h
> @@ -2,6 +2,7 @@
> //
> // Copyright 2019 Madhavan Srinivasan, IBM Corporation.
>
> +void unregister_power_pmu(struct power_pmu *pmu);
> int __init init_ppc970_pmu(void);
> int __init init_power5_pmu(void);
> int __init init_power5p_pmu(void);
> diff --git a/arch/powerpc/perf/isa207-common.c
> b/arch/powerpc/perf/isa207-common.c
> index 2b3547fdba4a..e11d1bbbc27b 100644
> --- a/arch/powerpc/perf/isa207-common.c
> +++ b/arch/powerpc/perf/isa207-common.c
> @@ -7,6 +7,7 @@
> * Copyright 2016 Madhavan Srinivasan, IBM Corporation.
> */
> #include "isa207-common.h"
> +#include <asm/dts_pmu.h>
>
> PMU_FORMAT_ATTR(event, "config:0-49");
> PMU_FORMAT_ATTR(pmcxsel, "config:0-7");
> diff --git a/arch/powerpc/platforms/powernv/opal.c
> b/arch/powerpc/platforms/powernv/opal.c
> index 1946dbdc9fa1..35be67eef2c6 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -947,6 +947,18 @@ static void __init opal_imc_init_dev(void)
>
> of_node_put(np);
> }
> +#define PMU_DTB "ibm,power-pmu"
> +
> +static void __init opal_pmus_init_dev(void)
> +{
> + struct device_node *np;
> +
> + np = of_find_compatible_node(NULL, NULL, PMU_DTB);
> + if (np)
> + of_platform_device_create(np, NULL, NULL);
> +
> + of_node_put(np);
> +}
>
> static int kopald(void *unused)
> {
> @@ -1032,6 +1044,9 @@ static int __init opal_init(void)
> /* Detect In-Memory Collection counters and create devices*/
> opal_imc_init_dev();
>
> + /*Detect PMU node and create device*/
.... with completely broken indentation style.
> + opal_pmus_init_dev();
> +
> /* Create leds platform devices */
> leds = of_find_node_by_path("/ibm,opal/leds");
> if (leds) {
Best regards,
Krzysztof